"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>担任淘宝产品总负责人的双十一,是怎样的体验?</h2>\\t\\t\\n\\t<time datetime=\\"2020-11-11T15:59:43+00:00\\" class=\\"by-line\\">11 Nov 2020, 杭州 | 作者 麦克船长 | 总计 138 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\\n\\n<p><img src=\\"/img/src/2020-11-11-captain-double-eleven-1.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-2.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-3.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-4.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-5.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-6.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-7.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-8.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-9.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-10.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-11.jpg\\" alt=\\"image\\" /></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。","url":"/2020/11/11/captain-double-eleven/","collection":"posts","relative_path":"_posts/2020-11-11-captain-double-eleven.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"担任淘宝产品总负责人的双十一,是怎样的体验?","date":"2020-11-11 15:59:43 +0000","tags":["思考"],"description":"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。","location":"杭州","slug":"captain-double-eleven","ext":".markdown"},{"next":{"path":"_posts/2020-11-11-captain-double-eleven.markdown","id":"/2020/11/11/captain-double-eleven","url":"/2020/11/11/captain-double-eleven/","collection":"posts","relative_path":"_posts/2020-11-11-captain-double-eleven.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"担任淘宝产品总负责人的双十一,是怎样的体验?","date":"2020-11-11 15:59:43 +0000","tags":["思考"],"description":"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。","location":"杭州","slug":"captain-double-eleven","ext":".markdown"},"path":"_posts/2020-04-15-covid2019-catering-business-mode.markdown","id":"/2020/04/14/covid2019-catering-business-mode","previous":{"path":"_posts/2020-04-11-delayed-gratification.markdown","id":"/2020/04/11/delayed-gratification","url":"/2020/04/11/delayed-gratification/","collection":"posts","relative_path":"_posts/2020-04-11-delayed-gratification.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"延迟满足,才有自由","date":"2020-04-11 06:18:03 +0000","tags":["思考"],"description":"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。","location":"杭州","slug":"delayed-gratification","ext":".markdown"},"content":"<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\\n\\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\\n\\n<ul>\\n <li>外卖</li>\\n <li>做饭</li>\\n <li>速食</li>\\n</ul>\\n\\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\\n\\n<h3 id=\\"标品化外卖业务\\">标品化外卖业务</h3>\\n\\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\\n\\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\\n\\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\\n\\n<ul>\\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\\n</ul>\\n\\n<h3 id=\\"社区生鲜前置仓\\">社区生鲜前置仓</h3>\\n\\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\\n\\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\\n\\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\\n\\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\\n\\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\\n\\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\\n</ul>\\n\\n<h3 id=\\"线上预包装食品\\">线上预包装食品</h3>\\n\\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\\n\\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\\n\\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\\n</ul>\\n\\n<h3 id=\\"线下重构新餐饮时代到来\\">线下重构,新餐饮时代到来</h3>\\n\\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\\n\\n<ul>\\n <li>用「标品化外卖」覆盖外卖场景</li>\\n <li>用「生鲜前置仓」覆盖做饭场景</li>\\n <li>用「预包装食品」覆盖速食场景</li>\\n</ul>\\n\\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\\n\\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\\n\\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>疫后怎么做餐饮品牌?三叉戟模式或成标配</title>\\n \\t<meta name=\\"description\\" content=\\"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>疫后怎么做餐饮品牌?三叉戟模式或成标配</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-14T16:42:43+00:00\\" class=\\"by-line\\">14 Apr 2020, 杭州 | 作者 麦克船长 | 总计 1845 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\\n\\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\\n\\n<ul>\\n <li>外卖</li>\\n <li>做饭</li>\\n <li>速食</li>\\n</ul>\\n\\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\\n\\n<h3 id=\\"标品化外卖业务\\">标品化外卖业务</h3>\\n\\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\\n\\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\\n\\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\\n\\n<ul>\\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\\n</ul>\\n\\n<h3 id=\\"社区生鲜前置仓\\">社区生鲜前置仓</h3>\\n\\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\\n\\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\\n\\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\\n\\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\\n\\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\\n\\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\\n</ul>\\n\\n<h3 id=\\"线上预包装食品\\">线上预包装食品</h3>\\n\\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\\n\\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\\n\\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\\n</ul>\\n\\n<h3 id=\\"线下重构新餐饮时代到来\\">线下重构,新餐饮时代到来</h3>\\n\\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\\n\\n<ul>\\n <li>用「标品化外卖」覆盖外卖场景</li>\\n <li>用「生鲜前置仓」覆盖做饭场景</li>\\n <li>用「预包装食品」覆盖速食场景</li>\\n</ul>\\n\\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\\n\\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\\n\\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题 ……","url":"/2020/04/14/covid2019-catering-business-mode/","collection":"posts","relative_path":"_posts/2020-04-15-covid2019-catering-business-mode.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"疫后怎么做餐饮品牌?三叉戟模式或成标配","date":"2020-04-14 16:42:43 +0000","tags":["思考"],"description":"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?","location":"杭州","slug":"covid2019-catering-business-mode","ext":".markdown"},{"next":{"path":"_posts/2020-04-15-covid2019-catering-business-mode.markdown","id":"/2020/04/14/covid2019-catering-business-mode","url":"/2020/04/14/covid2019-catering-business-mode/","collection":"posts","relative_path":"_posts/2020-04-15-covid2019-catering-business-mode.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"疫后怎么做餐饮品牌?三叉戟模式或成标配","date":"2020-04-14 16:42:43 +0000","tags":["思考"],"description":"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?","location":"杭州","slug":"covid2019-catering-business-mode","ext":".markdown"},"path":"_posts/2020-04-11-delayed-gratification.markdown","id":"/2020/04/11/delayed-gratification","previous":{"path":"_posts/2017-02-24-ai-make-people-life-as-billionaires.markdown","id":"/2017/02/23/ai-make-people-life-as-billionaires","url":"/2017/02/23/ai-make-people-life-as-billionaires/","collection":"posts","relative_path":"_posts/2017-02-24-ai-make-people-life-as-billionaires.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"未来人工智能就是要:让普通人过上现在富豪们的生活","date":"2017-02-23 18:23:33 +0000","tags":["思考"],"description":"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。","location":"北京","slug":"ai-make-people-life-as-billionaires","ext":".markdown"},"content":"<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\\n\\n<h3 id=\\"1儿童时期的延迟满足\\">1、儿童时期的延迟满足</h3>\\n\\n<h4 id=\\"棉花糖实验\\">棉花糖实验</h4>\\n\\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\\n\\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\\n\\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\\n\\n<p>先讲两个我自己的例子吧。</p>\\n\\n<h4 id=\\"黑加仑零食\\">黑加仑零食</h4>\\n\\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\\n\\n<h4 id=\\"暑假作业\\">暑假作业</h4>\\n\\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\\n\\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\\n\\n<h4 id=\\"延迟满足的背后是意志力自控力\\">延迟满足的背后,是意志力/自控力</h4>\\n\\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\\n\\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\\n\\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\\n\\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\\n\\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\\n\\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\\n\\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\\n\\n<h3 id=\\"2快乐理论挑战延迟满足\\">2、快乐理论,挑战延迟满足</h3>\\n\\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\\n\\n<h4 id=\\"烂苹果\\">烂苹果</h4>\\n\\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\\n\\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\\n\\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\\n\\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\\n\\n<h4 id=\\"快乐理论\\">快乐理论</h4>\\n\\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\\n\\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\\n\\n<p>但这个理论角度对吗?</p>\\n\\n<h3 id=\\"3我们应该在哪个范畴内讨论延迟满足\\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\\n\\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\\n\\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\\n\\n<h4 id=\\"仅有正反馈刺激的奶头乐\\">仅有正反馈刺激的奶头乐</h4>\\n\\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\\n\\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\\n\\n<h4 id=\\"延迟满足重在顺序而非时间\\">延迟满足重在顺序,而非时间</h4>\\n\\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\\n\\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\\n\\n<h3 id=\\"4常见的延迟满足与即时满足\\">4、常见的延迟满足与即时满足</h3>\\n\\n<h4 id=\\"初阶对手vs浅度延迟满足\\">初阶对手 vs 浅度延迟满足</h4>\\n\\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\\n\\n<ul>\\n <li>\\n <p>睡懒觉</p>\\n </li>\\n <li>\\n <p>过量饮食</p>\\n </li>\\n <li>\\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\\n </li>\\n <li>\\n <p>冲动情绪</p>\\n </li>\\n <li>\\n <p>炫耀(粗俗说就是装B)</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\\n\\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\\n\\n<h4 id=\\"中阶对手vs中度延迟满足\\">中阶对手 vs 中度延迟满足</h4>\\n\\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\\n\\n<ul>\\n <li>\\n <p>在家边看电视/视频,边学习/工作</p>\\n </li>\\n <li>\\n <p>依赖二手知识,而怠于一手知识</p>\\n </li>\\n <li>\\n <p>刷知乎、行业资讯 APP</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>例子会有很多,我们仅稍微说下这三个。</p>\\n\\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\\n\\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\\n\\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\\n\\n<h4 id=\\"高阶对手vs深度延迟满足\\">高阶对手 vs 深度延迟满足</h4>\\n\\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\\n\\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\\n\\n<p>我这里就说一个影响很大的:</p>\\n\\n<p>* 急于行动</p>\\n\\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\\n\\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\\n\\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\\n\\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\\n\\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\\n\\n<h3 id=\\"5延迟满足的驻留时长\\">5、延迟满足的驻留时长</h3>\\n\\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\\n\\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\\n\\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\\n\\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\\n\\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\\n\\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\\n\\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\\n\\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>延迟满足,才有自由</title>\\n \\t<meta name=\\"description\\" content=\\"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>延迟满足,才有自由</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-11T06:18:03+00:00\\" class=\\"by-line\\">11 Apr 2020, 杭州 | 作者 麦克船长 | 总计 4478 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\\n\\n<h3 id=\\"1儿童时期的延迟满足\\">1、儿童时期的延迟满足</h3>\\n\\n<h4 id=\\"棉花糖实验\\">棉花糖实验</h4>\\n\\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\\n\\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\\n\\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\\n\\n<p>先讲两个我自己的例子吧。</p>\\n\\n<h4 id=\\"黑加仑零食\\">黑加仑零食</h4>\\n\\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\\n\\n<h4 id=\\"暑假作业\\">暑假作业</h4>\\n\\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\\n\\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\\n\\n<h4 id=\\"延迟满足的背后是意志力自控力\\">延迟满足的背后,是意志力/自控力</h4>\\n\\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\\n\\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\\n\\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\\n\\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\\n\\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\\n\\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\\n\\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\\n\\n<h3 id=\\"2快乐理论挑战延迟满足\\">2、快乐理论,挑战延迟满足</h3>\\n\\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\\n\\n<h4 id=\\"烂苹果\\">烂苹果</h4>\\n\\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\\n\\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\\n\\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\\n\\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\\n\\n<h4 id=\\"快乐理论\\">快乐理论</h4>\\n\\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\\n\\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\\n\\n<p>但这个理论角度对吗?</p>\\n\\n<h3 id=\\"3我们应该在哪个范畴内讨论延迟满足\\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\\n\\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\\n\\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\\n\\n<h4 id=\\"仅有正反馈刺激的奶头乐\\">仅有正反馈刺激的奶头乐</h4>\\n\\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\\n\\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\\n\\n<h4 id=\\"延迟满足重在顺序而非时间\\">延迟满足重在顺序,而非时间</h4>\\n\\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\\n\\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\\n\\n<h3 id=\\"4常见的延迟满足与即时满足\\">4、常见的延迟满足与即时满足</h3>\\n\\n<h4 id=\\"初阶对手vs浅度延迟满足\\">初阶对手 vs 浅度延迟满足</h4>\\n\\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\\n\\n<ul>\\n <li>\\n <p>睡懒觉</p>\\n </li>\\n <li>\\n <p>过量饮食</p>\\n </li>\\n <li>\\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\\n </li>\\n <li>\\n <p>冲动情绪</p>\\n </li>\\n <li>\\n <p>炫耀(粗俗说就是装B)</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\\n\\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\\n\\n<h4 id=\\"中阶对手vs中度延迟满足\\">中阶对手 vs 中度延迟满足</h4>\\n\\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\\n\\n<ul>\\n <li>\\n <p>在家边看电视/视频,边学习/工作</p>\\n </li>\\n <li>\\n <p>依赖二手知识,而怠于一手知识</p>\\n </li>\\n <li>\\n <p>刷知乎、行业资讯 APP</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>例子会有很多,我们仅稍微说下这三个。</p>\\n\\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\\n\\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\\n\\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\\n\\n<h4 id=\\"高阶对手vs深度延迟满足\\">高阶对手 vs 深度延迟满足</h4>\\n\\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\\n\\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\\n\\n<p>我这里就说一个影响很大的:</p>\\n\\n<p>* 急于行动</p>\\n\\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\\n\\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\\n\\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\\n\\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\\n\\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\\n\\n<h3 id=\\"5延迟满足的驻留时长\\">5、延迟满足的驻留时长</h3>\\n\\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\\n\\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\\n\\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\\n\\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\\n\\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\\n\\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\\n\\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\\n\\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上 ……","url":"/2020/04/11/delayed-gratification/","collection":"posts","relative_path":"_posts/2020-04-11-delayed-gratification.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"延迟满足,才有自由","date":"2020-04-11 06:18:03 +0000","tags":["思考"],"description":"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。","location":"杭州","slug":"delayed-gratification","ext":".markdown"},{"next":{"path":"_posts/2020-04-11-delayed-gratification.markdown","id":"/2020/04/11/delayed-gratification","url":"/2020/04/11/delayed-gratification/","collection":"posts","relative_path":"_posts/2020-04-11-delayed-gratification.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"延迟满足,才有自由","date":"2020-04-11 06:18:03 +0000","tags":["思考"],"description":"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。","location":"杭州","slug":"delayed-gratification","ext":".markdown"},"path":"_posts/2017-02-24-ai-make-people-life-as-billionaires.markdown","id":"/2017/02/23/ai-make-people-life-as-billionaires","previous":{"path":"_posts/2017-02-01-danshari-vs-remember-reverberations.markdown","id":"/2017/01/31/danshari-vs-remember-reverberations","url":"/2017/01/31/danshari-vs-remember-reverberations/","collection":"posts","relative_path":"_posts/2017-02-01-danshari-vs-remember-reverberations.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"我们是应该「断舍离」还是「念念不忘,必有回响」","date":"2017-01-31 20:59:21 +0000","tags":["思考"],"description":"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。","location":"北京","slug":"danshari-vs-remember-reverberations","ext":".markdown"},"content":"<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\\n\\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\\n\\n<h4 id=\\"但是我没有钱呢\\">但是我没有钱呢?</h4>\\n\\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\\n\\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\\n\\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\\n\\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\\n\\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\\n\\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\\n\\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>未来人工智能就是要:让普通人过上现在富豪们的生活</title>\\n \\t<meta name=\\"description\\" content=\\"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>未来人工智能就是要:让普通人过上现在富豪们的生活</h2>\\t\\t\\n\\t<time datetime=\\"2017-02-23T18:23:33+00:00\\" class=\\"by-line\\">23 Feb 2017, 北京 | 作者 麦克船长 | 总计 627 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\\n\\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\\n\\n<h4 id=\\"但是我没有钱呢\\">但是我没有钱呢?</h4>\\n\\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\\n\\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\\n\\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\\n\\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\\n\\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\\n\\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\\n\\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域 ……","url":"/2017/02/23/ai-make-people-life-as-billionaires/","collection":"posts","relative_path":"_posts/2017-02-24-ai-make-people-life-as-billionaires.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"未来人工智能就是要:让普通人过上现在富豪们的生活","date":"2017-02-23 18:23:33 +0000","tags":["思考"],"description":"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。","location":"北京","slug":"ai-make-people-life-as-billionaires","ext":".markdown"},{"next":{"path":"_posts/2017-02-24-ai-make-people-life-as-billionaires.markdown","id":"/2017/02/23/ai-make-people-life-as-billionaires","url":"/2017/02/23/ai-make-people-life-as-billionaires/","collection":"posts","relative_path":"_posts/2017-02-24-ai-make-people-life-as-billionaires.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"未来人工智能就是要:让普通人过上现在富豪们的生活","date":"2017-02-23 18:23:33 +0000","tags":["思考"],"description":"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。","location":"北京","slug":"ai-make-people-life-as-billionaires","ext":".markdown"},"path":"_posts/2017-02-01-danshari-vs-remember-reverberations.markdown","id":"/2017/01/31/danshari-vs-remember-reverberations","previous":{"path":"_posts/2012-08-04-openrtmfp-cumulus-9.markdown","id":"/2012/08/04/openrtmfp-cumulus-9","url":"/2012/08/04/openrtmfp-cumulus-9/","collection":"posts","relative_path":"_posts/2012-08-04-openrtmfp-cumulus-9.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析","date":"2012-08-04 17:58:17 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。","location":"广州","slug":"openrtmfp-cumulus-9","ext":".markdown"},"content":"<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"引子\\">引子</h3>\\n\\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\\n\\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\\n\\n<p>「嗨,我说老朱,讲讲你啊!」</p>\\n\\n<p>「我就不提啦。」</p>\\n\\n<p>「为什么啊?说说,说说!」</p>\\n\\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\\n\\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\\n\\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\\n\\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\\n\\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\\n\\n<blockquote>\\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\\n</blockquote>\\n\\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\\n\\n<h3 id=\\"断舍离\\">断舍离</h3>\\n\\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\\" alt=\\"image\\" /></p>\\n\\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\\n\\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\\n\\n<h3 id=\\"念念不忘必有回响\\">念念不忘,必有回响</h3>\\n\\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\\" alt=\\"image\\" /></p>\\n\\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\\n\\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\\n\\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\\n\\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\\n\\n<h3 id=\\"用正负反馈来判断何时何为\\">用「正负反馈」来判断何时何为?</h3>\\n\\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\\n\\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\\n\\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\\n\\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\\n\\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\\n\\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\\n\\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\\n\\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\\n\\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\\n\\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\\n\\n<p>然后,我们一起等待,那声回响。</p>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>我们是应该「断舍离」还是「念念不忘,必有回响」</title>\\n \\t<meta name=\\"description\\" content=\\"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>我们是应该「断舍离」还是「念念不忘,必有回响」</h2>\\t\\t\\n\\t<time datetime=\\"2017-01-31T20:59:21+00:00\\" class=\\"by-line\\">31 Jan 2017, 北京 | 作者 麦克船长 | 总计 2577 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"引子\\">引子</h3>\\n\\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\\n\\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\\n\\n<p>「嗨,我说老朱,讲讲你啊!」</p>\\n\\n<p>「我就不提啦。」</p>\\n\\n<p>「为什么啊?说说,说说!」</p>\\n\\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\\n\\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\\n\\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\\n\\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\\n\\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\\n\\n<blockquote>\\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\\n</blockquote>\\n\\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\\n\\n<h3 id=\\"断舍离\\">断舍离</h3>\\n\\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\\" alt=\\"image\\" /></p>\\n\\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\\n\\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\\n\\n<h3 id=\\"念念不忘必有回响\\">念念不忘,必有回响</h3>\\n\\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\\" alt=\\"image\\" /></p>\\n\\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\\n\\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\\n\\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\\n\\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\\n\\n<h3 id=\\"用正负反馈来判断何时何为\\">用「正负反馈」来判断何时何为?</h3>\\n\\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\\n\\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\\n\\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\\n\\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\\n\\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\\n\\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\\n\\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\\n\\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\\n\\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\\n\\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\\n\\n<p>然后,我们一起等待,那声回响。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」 ……","url":"/2017/01/31/danshari-vs-remember-reverberations/","collection":"posts","relative_path":"_posts/2017-02-01-danshari-vs-remember-reverberations.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"我们是应该「断舍离」还是「念念不忘,必有回响」","date":"2017-01-31 20:59:21 +0000","tags":["思考"],"description":"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。","location":"北京","slug":"danshari-vs-remember-reverberations","ext":".markdown"},{"next":{"path":"_posts/2017-02-01-danshari-vs-remember-reverberations.markdown","id":"/2017/01/31/danshari-vs-remember-reverberations","url":"/2017/01/31/danshari-vs-remember-reverberations/","collection":"posts","relative_path":"_posts/2017-02-01-danshari-vs-remember-reverberations.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"我们是应该「断舍离」还是「念念不忘,必有回响」","date":"2017-01-31 20:59:21 +0000","tags":["思考"],"description":"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。","location":"北京","slug":"danshari-vs-remember-reverberations","ext":".markdown"},"path":"_posts/2012-08-04-openrtmfp-cumulus-9.markdown","id":"/2012/08/04/openrtmfp-cumulus-9","previous":{"path":"_posts/2012-07-23-openrtmfp-cumulus-8.markdown","id":"/2012/07/23/openrtmfp-cumulus-8","url":"/2012/07/23/openrtmfp-cumulus-8/","collection":"posts","relative_path":"_posts/2012-07-23-openrtmfp-cumulus-8.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点","date":"2012-07-23 03:07:43 +0000","tags":["直播技术"],"description":"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。","location":"广州","slug":"openrtmfp-cumulus-8","ext":".markdown"},"content":"<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfpserver-线程的启动和等待\\" id=\\"markdown-toc-一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</a> <ul>\\n <li><a href=\\"#1pocothread\\" id=\\"markdown-toc-1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></a></li>\\n <li><a href=\\"#2封装一个可运行线程的类\\" id=\\"markdown-toc-2封装一个可运行线程的类\\">2、封装一个可运行线程的类</a></li>\\n <li><a href=\\"#3启动-rtmfpserver-线程\\" id=\\"markdown-toc-3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</a></li>\\n <li><a href=\\"#4rtmfpserver-线程等待\\" id=\\"markdown-toc-4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二rtmfpmanager-对-rtmfpserver-的影响\\" id=\\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</a></li>\\n</ul>\\n\\n<h3 id=\\"一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</h3>\\n\\n<h4 id=\\"1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></h4>\\n\\n<p>Cumulus 大量使用了 <code class=\\"language-plaintext highlighter-rouge\\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PoechantRunnable</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// your codes</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">};</span>\\n \\n<span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">PoechantRunnable</span> <span class=\\"n\\">runnable</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Image that it's a gift</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">thread</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// And… thread is just like your girl</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">runnable</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Okay, give your sweet babe the gift :)</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2封装一个可运行线程的类\\">2、封装一个可运行线程的类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中实现了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 类,该类继承了 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,就是上面那个 <code class=\\"language-plaintext highlighter-rouge\\">gift</code> 喽。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">StartableProcess</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span><span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">);</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>可以看到其中有 <code class=\\"language-plaintext highlighter-rouge\\">Startable&amp; _startable</code> 引用成员,它并没有继承 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,而是封装了 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">_thread</span><span class=\\"p\\">;</span>\\n<span class=\\"n\\">StartableProcess</span> <span class=\\"n\\">_process</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>这里 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 封装了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 成员,与 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\\n\\n<h4 id=\\"3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</h4>\\n<p>我们可以看到在 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的构造函数中初始化了 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\\"language-plaintext highlighter-rouge\\">(Flag Field)_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,因为它还没有调用启动函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_name</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_stop</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_process</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>初始化 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 时,调用 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">StartableProcess</span><span class=\\"o\\">::</span><span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">(</span><span class=\\"n\\">startable</span><span class=\\"p\\">){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>传入 <code class=\\"language-plaintext highlighter-rouge\\">_startable</code> 的引用。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的,然后通过调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 来启动,启动后会响应到 <code class=\\"language-plaintext highlighter-rouge\\">run()</code>。下面我们以 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程为例。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 类是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPServer</span>\\n <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Gateway</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">protected</span> <span class=\\"n\\">Handler</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">SocketHandler</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">RTMFPServer</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">cores</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">(</span><span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>所在线程</th>\\n <th>调用者</th>\\n <th>函数</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>主线程</td>\\n <td>main(…)</td>\\n <td> </td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer从Startable继承来的Thread成员</td>\\n <td>Thread::start(…)</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\\n <td>StartableProcess::run()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::run()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</h4>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"n\\">terminate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 函数很简单,如下只进行了 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">giveHandle()</code> 两个操作。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">){</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">()</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">giveHandle</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的,声明如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">WakeUpType</span> <span class=\\"nf\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>在运行状态下,<code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,而默认参数 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code> 为 0,所以会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>这个 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员是一个 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 对象,<code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::wait()</code> 后,会一直等待 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\\"language-plaintext highlighter-rouge\\">wait</code> 的状态。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">set</code> 的动作是由:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::requestHandle()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">PoolThread::push(Poco::AutoPtr&lt;RunnableType&gt;&amp; pRunnable)</code></li>\\n</ul>\\n\\n<p>执行的。</p>\\n\\n<h3 id=\\"二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 同样,继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPManager</span> <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Task</span><span class=\\"p\\">,</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span>\\n</code></pre></div></div>\\n\\n<p>在构造函数中将 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\\"language-plaintext highlighter-rouge\\">_server</code> 引用成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPManager</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">server</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_server</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Task</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPManager\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"cm\\">/* ...... */</span>\\n\\n<span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_server</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的构造函数中调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 成员函数,是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的线程。然后响应到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager::run()</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">PRIO_LOW</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">2000</span><span class=\\"p\\">)</span><span class=\\"o\\">!=</span><span class=\\"n\\">STOP</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">waitHandle</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里要强调的是,这里的 <code class=\\"language-plaintext highlighter-rouge\\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\\n\\n<p>在这里我们可以看到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的主循环的等待时间的。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>你可以自行修改 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 的参数,这样就会调用 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code>)来进行睡眠。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\\"language-plaintext highlighter-rouge\\">handle</code> 成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handle</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_server</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里就会调用到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 源码时知道 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code> 函数并不是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程内运行的,而是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">manage</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\\"language-plaintext highlighter-rouge\\">Session</code>。</p>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-08-04T17:58:17+00:00\\" class=\\"by-line\\">04 Aug 2012, 广州 | 作者 麦克船长 | 总计 5236 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfpserver-线程的启动和等待\\" id=\\"markdown-toc-一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</a> <ul>\\n <li><a href=\\"#1pocothread\\" id=\\"markdown-toc-1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></a></li>\\n <li><a href=\\"#2封装一个可运行线程的类\\" id=\\"markdown-toc-2封装一个可运行线程的类\\">2、封装一个可运行线程的类</a></li>\\n <li><a href=\\"#3启动-rtmfpserver-线程\\" id=\\"markdown-toc-3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</a></li>\\n <li><a href=\\"#4rtmfpserver-线程等待\\" id=\\"markdown-toc-4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二rtmfpmanager-对-rtmfpserver-的影响\\" id=\\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</a></li>\\n</ul>\\n\\n<h3 id=\\"一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</h3>\\n\\n<h4 id=\\"1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></h4>\\n\\n<p>Cumulus 大量使用了 <code class=\\"language-plaintext highlighter-rouge\\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PoechantRunnable</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// your codes</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">};</span>\\n \\n<span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">PoechantRunnable</span> <span class=\\"n\\">runnable</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Image that it's a gift</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">thread</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// And… thread is just like your girl</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">runnable</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Okay, give your sweet babe the gift :)</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2封装一个可运行线程的类\\">2、封装一个可运行线程的类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中实现了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 类,该类继承了 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,就是上面那个 <code class=\\"language-plaintext highlighter-rouge\\">gift</code> 喽。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">StartableProcess</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span><span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">);</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>可以看到其中有 <code class=\\"language-plaintext highlighter-rouge\\">Startable&amp; _startable</code> 引用成员,它并没有继承 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,而是封装了 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">_thread</span><span class=\\"p\\">;</span>\\n<span class=\\"n\\">StartableProcess</span> <span class=\\"n\\">_process</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>这里 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 封装了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 成员,与 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\\n\\n<h4 id=\\"3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</h4>\\n<p>我们可以看到在 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的构造函数中初始化了 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\\"language-plaintext highlighter-rouge\\">(Flag Field)_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,因为它还没有调用启动函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_name</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_stop</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_process</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>初始化 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 时,调用 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">StartableProcess</span><span class=\\"o\\">::</span><span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">(</span><span class=\\"n\\">startable</span><span class=\\"p\\">){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>传入 <code class=\\"language-plaintext highlighter-rouge\\">_startable</code> 的引用。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的,然后通过调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 来启动,启动后会响应到 <code class=\\"language-plaintext highlighter-rouge\\">run()</code>。下面我们以 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程为例。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 类是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPServer</span>\\n <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Gateway</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">protected</span> <span class=\\"n\\">Handler</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">SocketHandler</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">RTMFPServer</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">cores</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">(</span><span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>所在线程</th>\\n <th>调用者</th>\\n <th>函数</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>主线程</td>\\n <td>main(…)</td>\\n <td> </td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer从Startable继承来的Thread成员</td>\\n <td>Thread::start(…)</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\\n <td>StartableProcess::run()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::run()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</h4>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"n\\">terminate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 函数很简单,如下只进行了 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">giveHandle()</code> 两个操作。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">){</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">()</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">giveHandle</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的,声明如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">WakeUpType</span> <span class=\\"nf\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>在运行状态下,<code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,而默认参数 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code> 为 0,所以会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>这个 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员是一个 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 对象,<code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::wait()</code> 后,会一直等待 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\\"language-plaintext highlighter-rouge\\">wait</code> 的状态。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">set</code> 的动作是由:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::requestHandle()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">PoolThread::push(Poco::AutoPtr&lt;RunnableType&gt;&amp; pRunnable)</code></li>\\n</ul>\\n\\n<p>执行的。</p>\\n\\n<h3 id=\\"二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 同样,继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPManager</span> <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Task</span><span class=\\"p\\">,</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span>\\n</code></pre></div></div>\\n\\n<p>在构造函数中将 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\\"language-plaintext highlighter-rouge\\">_server</code> 引用成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPManager</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">server</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_server</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Task</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPManager\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"cm\\">/* ...... */</span>\\n\\n<span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_server</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的构造函数中调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 成员函数,是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的线程。然后响应到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager::run()</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">PRIO_LOW</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">2000</span><span class=\\"p\\">)</span><span class=\\"o\\">!=</span><span class=\\"n\\">STOP</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">waitHandle</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里要强调的是,这里的 <code class=\\"language-plaintext highlighter-rouge\\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\\n\\n<p>在这里我们可以看到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的主循环的等待时间的。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>你可以自行修改 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 的参数,这样就会调用 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code>)来进行睡眠。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\\"language-plaintext highlighter-rouge\\">handle</code> 成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handle</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_server</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里就会调用到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 源码时知道 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code> 函数并不是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程内运行的,而是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">manage</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\\"language-plaintext highlighter-rouge\\">Session</code>。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。","url":"/2012/08/04/openrtmfp-cumulus-9/","collection":"posts","relative_path":"_posts/2012-08-04-openrtmfp-cumulus-9.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析","date":"2012-08-04 17:58:17 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。","location":"广州","slug":"openrtmfp-cumulus-9","ext":".markdown"},{"next":{"path":"_posts/2012-08-04-openrtmfp-cumulus-9.markdown","id":"/2012/08/04/openrtmfp-cumulus-9","url":"/2012/08/04/openrtmfp-cumulus-9/","collection":"posts","relative_path":"_posts/2012-08-04-openrtmfp-cumulus-9.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析","date":"2012-08-04 17:58:17 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。","location":"广州","slug":"openrtmfp-cumulus-9","ext":".markdown"},"path":"_posts/2012-07-23-openrtmfp-cumulus-8.markdown","id":"/2012/07/23/openrtmfp-cumulus-8","previous":{"path":"_posts/2012-06-25-openrtmfp-cumulus-7.markdown","id":"/2012/06/25/openrtmfp-cumulus-7","url":"/2012/06/25/openrtmfp-cumulus-7/","collection":"posts","relative_path":"_posts/2012-06-25-openrtmfp-cumulus-7.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法","date":"2012-06-25 02:56:26 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。","location":"广州","slug":"openrtmfp-cumulus-7","ext":".markdown"},"content":"<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1客户端发布publishing-on-client-side\\" id=\\"markdown-toc-1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</a></li>\\n <li><a href=\\"#2服务器端server-side\\" id=\\"markdown-toc-2服务器端server-side\\">2、服务器端(Server-side)</a></li>\\n <li><a href=\\"#3客户端订阅subscribing-on-client-side\\" id=\\"markdown-toc-3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</a></li>\\n <li><a href=\\"#4reference\\" id=\\"markdown-toc-4reference\\">4、Reference</a></li>\\n</ul>\\n\\n<p>整个流程概括如下:</p>\\n\\n<p>Flash 客户端通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 建立连接,然后通过 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\\n\\n<h3 id=\\"1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</h3>\\n\\n<p>通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\\"/2012/04/10/openrtmfp-cumulus-1/\\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\\"language-plaintext highlighter-rouge\\">nc</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost:1935\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\\"language-plaintext highlighter-rouge\\">ns1</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">ns1</span><span class=\\"p\\">.</span><span class=\\"nx\\">publish</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"poechant_media_flow\\"</span><span class=\\"p\\">,</span> <span class=\\"s2\\">\\"live\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\\n\\n<h3 id=\\"2服务器端server-side\\">2、服务器端(Server-side)</h3>\\n\\n<p>Cumulus 通过 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPReceiving</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">rtmfpReceiving</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\\n\\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\\"language-plaintext highlighter-rouge\\">Handshake</code> 类的 <code class=\\"language-plaintext highlighter-rouge\\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ServerSession</span><span class=\\"o\\">::</span><span class=\\"n\\">packetHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Flow</span><span class=\\"o\\">::</span><span class=\\"n\\">fragmentSortedHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">stage</span><span class=\\"p\\">,</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">fragment</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">flags</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF_WITH_HANDLER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">messageHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">amf</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AUDIO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">audioHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">VIDEO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">videoHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">rawHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>接下来在 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushAudioPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushVideoPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushDataPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中的 <code class=\\"language-plaintext highlighter-rouge\\">_listeners</code> 就是该 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Listener</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">addListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">writer</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">unbuffered</span><span class=\\"p\\">);</span>\\n \\n<span class=\\"kt\\">void</span> <span class=\\"nf\\">removeListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这两个函数来实现的。</p>\\n\\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\\n\\n<h3 id=\\"3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</h3>\\n\\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>ns2.play(\\"poechant_media_flow\\");\\n</code></pre></div></div>\\n\\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\\"language-plaintext highlighter-rouge\\">NetStream::send(…)</code> 的,用于发送 <code class=\\"language-plaintext highlighter-rouge\\">Data</code>,<code class=\\"language-plaintext highlighter-rouge\\">Audio</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Video</code> 的程序可以参考该例修改。</p>\\n\\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 初始化的时候,传入 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\\n\\n<h3 id=\\"4reference\\">4、Reference</h3>\\n\\n<ul>\\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\\n</ul>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</title>\\n \\t<meta name=\\"description\\" content=\\"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</h2>\\t\\t\\n\\t<time datetime=\\"2012-07-23T03:07:43+00:00\\" class=\\"by-line\\">23 Jul 2012, 广州 | 作者 麦克船长 | 总计 3111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1客户端发布publishing-on-client-side\\" id=\\"markdown-toc-1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</a></li>\\n <li><a href=\\"#2服务器端server-side\\" id=\\"markdown-toc-2服务器端server-side\\">2、服务器端(Server-side)</a></li>\\n <li><a href=\\"#3客户端订阅subscribing-on-client-side\\" id=\\"markdown-toc-3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</a></li>\\n <li><a href=\\"#4reference\\" id=\\"markdown-toc-4reference\\">4、Reference</a></li>\\n</ul>\\n\\n<p>整个流程概括如下:</p>\\n\\n<p>Flash 客户端通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 建立连接,然后通过 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\\n\\n<h3 id=\\"1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</h3>\\n\\n<p>通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\\"/2012/04/10/openrtmfp-cumulus-1/\\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\\"language-plaintext highlighter-rouge\\">nc</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost:1935\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\\"language-plaintext highlighter-rouge\\">ns1</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">ns1</span><span class=\\"p\\">.</span><span class=\\"nx\\">publish</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"poechant_media_flow\\"</span><span class=\\"p\\">,</span> <span class=\\"s2\\">\\"live\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\\n\\n<h3 id=\\"2服务器端server-side\\">2、服务器端(Server-side)</h3>\\n\\n<p>Cumulus 通过 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPReceiving</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">rtmfpReceiving</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\\n\\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\\"language-plaintext highlighter-rouge\\">Handshake</code> 类的 <code class=\\"language-plaintext highlighter-rouge\\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ServerSession</span><span class=\\"o\\">::</span><span class=\\"n\\">packetHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Flow</span><span class=\\"o\\">::</span><span class=\\"n\\">fragmentSortedHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">stage</span><span class=\\"p\\">,</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">fragment</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">flags</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF_WITH_HANDLER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">messageHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">amf</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AUDIO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">audioHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">VIDEO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">videoHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">rawHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>接下来在 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushAudioPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushVideoPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushDataPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中的 <code class=\\"language-plaintext highlighter-rouge\\">_listeners</code> 就是该 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Listener</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">addListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">writer</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">unbuffered</span><span class=\\"p\\">);</span>\\n \\n<span class=\\"kt\\">void</span> <span class=\\"nf\\">removeListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这两个函数来实现的。</p>\\n\\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\\n\\n<h3 id=\\"3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</h3>\\n\\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>ns2.play(\\"poechant_media_flow\\");\\n</code></pre></div></div>\\n\\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\\"language-plaintext highlighter-rouge\\">NetStream::send(…)</code> 的,用于发送 <code class=\\"language-plaintext highlighter-rouge\\">Data</code>,<code class=\\"language-plaintext highlighter-rouge\\">Audio</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Video</code> 的程序可以参考该例修改。</p>\\n\\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 初始化的时候,传入 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\\n\\n<h3 id=\\"4reference\\">4、Reference</h3>\\n\\n<ul>\\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\\n</ul>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。","url":"/2012/07/23/openrtmfp-cumulus-8/","collection":"posts","relative_path":"_posts/2012-07-23-openrtmfp-cumulus-8.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点","date":"2012-07-23 03:07:43 +0000","tags":["直播技术"],"description":"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。","location":"广州","slug":"openrtmfp-cumulus-8","ext":".markdown"},{"next":{"path":"_posts/2012-07-23-openrtmfp-cumulus-8.markdown","id":"/2012/07/23/openrtmfp-cumulus-8","url":"/2012/07/23/openrtmfp-cumulus-8/","collection":"posts","relative_path":"_posts/2012-07-23-openrtmfp-cumulus-8.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点","date":"2012-07-23 03:07:43 +0000","tags":["直播技术"],"description":"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。","location":"广州","slug":"openrtmfp-cumulus-8","ext":".markdown"},"path":"_posts/2012-06-25-openrtmfp-cumulus-7.markdown","id":"/2012/06/25/openrtmfp-cumulus-7","previous":{"path":"_posts/2012-06-07-openrtmfp-cumulus-6.markdown","id":"/2012/06/07/openrtmfp-cumulus-6","url":"/2012/06/07/openrtmfp-cumulus-6/","collection":"posts","relative_path":"_posts/2012-06-07-openrtmfp-cumulus-6.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法","date":"2012-06-07 15:34:18 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。","location":"广州","slug":"openrtmfp-cumulus-6","ext":".markdown"},"content":"<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中的线程都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>,在其中封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 函数如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样一个类继承 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code>,然后调用到该类自己的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\\"language-plaintext highlighter-rouge\\">SocketManager</code> 为例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">SocketManager</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span> \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>我们要看看这个 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 是怎么回事,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">running</span><span class=\\"p\\">()</span> <span class=\\"k\\">const</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>很简单,就是通过 <code class=\\"language-plaintext highlighter-rouge\\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 是什么时候被设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 的呢?就是上面的 <code class=\\"language-plaintext highlighter-rouge\\">start()</code>,这里存在的一个问题就是先 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 线程,再设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_thread.start(_process);\\n_stop=false;\\n</code></pre></div></div>\\n\\n<p>而 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 之后 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 的时候就开始通过 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 来判断 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值了。所以你会在使用 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>它们是:</p>\\n\\n<ul>\\n <li>主线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程</li>\\n</ul>\\n\\n<p>而异常情况可能是 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动,甚至 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\\" alt=\\"image\\" /></p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动的情况 T.T</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\\n\\n<p>解决办法就是将 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span> \\n <span class=\\"c1\\">// June 25th, 2012, Michael@YY</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-25T02:56:26+00:00\\" class=\\"by-line\\">25 Jun 2012, 广州 | 作者 麦克船长 | 总计 2111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中的线程都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>,在其中封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 函数如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样一个类继承 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code>,然后调用到该类自己的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\\"language-plaintext highlighter-rouge\\">SocketManager</code> 为例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">SocketManager</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span> \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>我们要看看这个 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 是怎么回事,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">running</span><span class=\\"p\\">()</span> <span class=\\"k\\">const</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>很简单,就是通过 <code class=\\"language-plaintext highlighter-rouge\\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 是什么时候被设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 的呢?就是上面的 <code class=\\"language-plaintext highlighter-rouge\\">start()</code>,这里存在的一个问题就是先 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 线程,再设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_thread.start(_process);\\n_stop=false;\\n</code></pre></div></div>\\n\\n<p>而 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 之后 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 的时候就开始通过 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 来判断 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值了。所以你会在使用 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>它们是:</p>\\n\\n<ul>\\n <li>主线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程</li>\\n</ul>\\n\\n<p>而异常情况可能是 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动,甚至 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\\" alt=\\"image\\" /></p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动的情况 T.T</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\\n\\n<p>解决办法就是将 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span> \\n <span class=\\"c1\\">// June 25th, 2012, Michael@YY</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。","url":"/2012/06/25/openrtmfp-cumulus-7/","collection":"posts","relative_path":"_posts/2012-06-25-openrtmfp-cumulus-7.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法","date":"2012-06-25 02:56:26 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。","location":"广州","slug":"openrtmfp-cumulus-7","ext":".markdown"},{"next":{"path":"_posts/2012-06-25-openrtmfp-cumulus-7.markdown","id":"/2012/06/25/openrtmfp-cumulus-7","url":"/2012/06/25/openrtmfp-cumulus-7/","collection":"posts","relative_path":"_posts/2012-06-25-openrtmfp-cumulus-7.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法","date":"2012-06-25 02:56:26 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。","location":"广州","slug":"openrtmfp-cumulus-7","ext":".markdown"},"path":"_posts/2012-06-07-openrtmfp-cumulus-6.markdown","id":"/2012/06/07/openrtmfp-cumulus-6","previous":{"path":"_posts/2012-05-16-excellent-software-engineer-basic-skills.markdown","id":"/2012/05/15/excellent-software-engineer-basic-skills","url":"/2012/05/15/excellent-software-engineer-basic-skills/","collection":"posts","relative_path":"_posts/2012-05-16-excellent-software-engineer-basic-skills.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"一名出色软件工程师的技术基本功:编程与工具","date":"2012-05-15 17:06:59 +0000","tags":["思考"],"description":"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。","location":"广州","slug":"excellent-software-engineer-basic-skills","ext":".markdown"},"content":"<p>OpenRTMFP/Cumulus 提供了 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>。</p>\\n\\n<p>一般来说,Thread A 会准备好要 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息。</p>\\n\\n<p>但是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>由于在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">createAMFMessage</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n \\n <span class=\\"c1\\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"p\\">(</span><span class=\\"n\\">_closed</span> <span class=\\"o\\">||</span> <span class=\\"n\\">signature</span><span class=\\"p\\">.</span><span class=\\"n\\">empty</span><span class=\\"p\\">()</span> <span class=\\"o\\">||</span> <span class=\\"n\\">_band</span><span class=\\"p\\">.</span><span class=\\"n\\">failed</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">MessageBuffered</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"n\\">_MessageNull</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后再调用时最后再增加 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Mutex</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedLock</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">msgQueueMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就基本解决了这个线程安全问题。</p>\\n\\n<p>另外,使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-07T15:34:18+00:00\\" class=\\"by-line\\">07 Jun 2012, 广州 | 作者 麦克船长 | 总计 1538 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>OpenRTMFP/Cumulus 提供了 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>。</p>\\n\\n<p>一般来说,Thread A 会准备好要 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息。</p>\\n\\n<p>但是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>由于在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">createAMFMessage</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n \\n <span class=\\"c1\\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"p\\">(</span><span class=\\"n\\">_closed</span> <span class=\\"o\\">||</span> <span class=\\"n\\">signature</span><span class=\\"p\\">.</span><span class=\\"n\\">empty</span><span class=\\"p\\">()</span> <span class=\\"o\\">||</span> <span class=\\"n\\">_band</span><span class=\\"p\\">.</span><span class=\\"n\\">failed</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">MessageBuffered</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"n\\">_MessageNull</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后再调用时最后再增加 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Mutex</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedLock</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">msgQueueMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就基本解决了这个线程安全问题。</p>\\n\\n<p>另外,使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。","url":"/2012/06/07/openrtmfp-cumulus-6/","collection":"posts","relative_path":"_posts/2012-06-07-openrtmfp-cumulus-6.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法","date":"2012-06-07 15:34:18 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。","location":"广州","slug":"openrtmfp-cumulus-6","ext":".markdown"},{"next":{"path":"_posts/2012-06-07-openrtmfp-cumulus-6.markdown","id":"/2012/06/07/openrtmfp-cumulus-6","url":"/2012/06/07/openrtmfp-cumulus-6/","collection":"posts","relative_path":"_posts/2012-06-07-openrtmfp-cumulus-6.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法","date":"2012-06-07 15:34:18 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。","location":"广州","slug":"openrtmfp-cumulus-6","ext":".markdown"},"path":"_posts/2012-05-16-excellent-software-engineer-basic-skills.markdown","id":"/2012/05/15/excellent-software-engineer-basic-skills","previous":{"path":"_posts/2012-04-24-openrtmfp-cumulus-5.markdown","id":"/2012/04/24/openrtmfp-cumulus-5","url":"/2012/04/24/openrtmfp-cumulus-5/","collection":"posts","relative_path":"_posts/2012-04-24-openrtmfp-cumulus-5.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析","date":"2012-04-24 03:31:10 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。","location":"广州","slug":"openrtmfp-cumulus-5","ext":".markdown"},"content":"<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"0写在前面\\">0、写在前面</h3>\\n\\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\\n\\n<h3 id=\\"1编程\\">1、编程</h3>\\n\\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\\n\\n<h4 id=\\"如果你精通c语言\\">如果你精通 C 语言</h4>\\n\\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\\n\\n<h4 id=\\"如果你精通c语言-1\\">如果你精通 C++ 语言</h4>\\n\\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\\n\\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\\n\\n<h4 id=\\"精通的关键还是针对语言核心来说的\\">精通的关键,还是针对语言核心来说的。</h4>\\n\\n<p>第一,你要对这个语言的语法特性熟稔;</p>\\n\\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\\n\\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\\n\\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\\n\\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\\n\\n<h4 id=\\"与计算机网络的基础结构相关联的技术实现\\">与计算机、网络的基础结构相关联的技术实现</h4>\\n\\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\\n\\n<h3 id=\\"2工具\\">2、工具</h3>\\n\\n<p>对于工具有三个层面:</p>\\n\\n<p>第一,是熟练的使用一些工具。</p>\\n\\n<p>第二,是能够发现提高生产力的工具。</p>\\n\\n<p>第三,是能够在无可用工具时自己编写工具。</p>\\n\\n<p>那么都有哪些最最最基本的工具呢?</p>\\n\\n<h4 id=\\"ideintegrateddevelopmentenvironment\\">IDE(Integrated Development Environment)</h4>\\n\\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\\n\\n<h4 id=\\"vcsversioncontrolsystem\\">VCS(Version Control System)</h4>\\n\\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\\n\\n<blockquote>\\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black &amp; white.</p>\\n</blockquote>\\n\\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\\n\\n<h4 id=\\"clicommandlineinterface\\">CLI(Command Line Interface)</h4>\\n\\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\\n\\n<h3 id=\\"3结语\\">3、结语</h3>\\n\\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>一名出色软件工程师的技术基本功:编程与工具</title>\\n \\t<meta name=\\"description\\" content=\\"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>一名出色软件工程师的技术基本功:编程与工具</h2>\\t\\t\\n\\t<time datetime=\\"2012-05-15T17:06:59+00:00\\" class=\\"by-line\\">15 May 2012, 广州 | 作者 麦克船长 | 总计 2132 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"0写在前面\\">0、写在前面</h3>\\n\\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\\n\\n<h3 id=\\"1编程\\">1、编程</h3>\\n\\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\\n\\n<h4 id=\\"如果你精通c语言\\">如果你精通 C 语言</h4>\\n\\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\\n\\n<h4 id=\\"如果你精通c语言-1\\">如果你精通 C++ 语言</h4>\\n\\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\\n\\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\\n\\n<h4 id=\\"精通的关键还是针对语言核心来说的\\">精通的关键,还是针对语言核心来说的。</h4>\\n\\n<p>第一,你要对这个语言的语法特性熟稔;</p>\\n\\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\\n\\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\\n\\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\\n\\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\\n\\n<h4 id=\\"与计算机网络的基础结构相关联的技术实现\\">与计算机、网络的基础结构相关联的技术实现</h4>\\n\\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\\n\\n<h3 id=\\"2工具\\">2、工具</h3>\\n\\n<p>对于工具有三个层面:</p>\\n\\n<p>第一,是熟练的使用一些工具。</p>\\n\\n<p>第二,是能够发现提高生产力的工具。</p>\\n\\n<p>第三,是能够在无可用工具时自己编写工具。</p>\\n\\n<p>那么都有哪些最最最基本的工具呢?</p>\\n\\n<h4 id=\\"ideintegrateddevelopmentenvironment\\">IDE(Integrated Development Environment)</h4>\\n\\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\\n\\n<h4 id=\\"vcsversioncontrolsystem\\">VCS(Version Control System)</h4>\\n\\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\\n\\n<blockquote>\\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black &amp; white.</p>\\n</blockquote>\\n\\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\\n\\n<h4 id=\\"clicommandlineinterface\\">CLI(Command Line Interface)</h4>\\n\\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\\n\\n<h3 id=\\"3结语\\">3、结语</h3>\\n\\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解 ……","url":"/2012/05/15/excellent-software-engineer-basic-skills/","collection":"posts","relative_path":"_posts/2012-05-16-excellent-software-engineer-basic-skills.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"一名出色软件工程师的技术基本功:编程与工具","date":"2012-05-15 17:06:59 +0000","tags":["思考"],"description":"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。","location":"广州","slug":"excellent-software-engineer-basic-skills","ext":".markdown"},{"next":{"path":"_posts/2012-05-16-excellent-software-engineer-basic-skills.markdown","id":"/2012/05/15/excellent-software-engineer-basic-skills","url":"/2012/05/15/excellent-software-engineer-basic-skills/","collection":"posts","relative_path":"_posts/2012-05-16-excellent-software-engineer-basic-skills.markdown","draft":false,"categories":["thinking"],"layout":"post","title":"一名出色软件工程师的技术基本功:编程与工具","date":"2012-05-15 17:06:59 +0000","tags":["思考"],"description":"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。","location":"广州","slug":"excellent-software-engineer-basic-skills","ext":".markdown"},"path":"_posts/2012-04-24-openrtmfp-cumulus-5.markdown","id":"/2012/04/24/openrtmfp-cumulus-5","previous":{"path":"_posts/2012-04-24-openrtmfp-cumulus-4.markdown","id":"/2012/04/24/openrtmfp-cumulus-4","url":"/2012/04/24/openrtmfp-cumulus-4/","collection":"posts","relative_path":"_posts/2012-04-24-openrtmfp-cumulus-4.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析","date":"2012-04-24 02:04:55 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。","location":"广州","slug":"openrtmfp-cumulus-4","ext":".markdown"},"content":"<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一流缓冲区\\" id=\\"markdown-toc-一流缓冲区\\">一、流缓冲区</a> <ul>\\n <li><a href=\\"#1了解-stdstreambuf\\" id=\\"markdown-toc-1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></a> <ul>\\n <li><a href=\\"#11单步移动内置指针\\" id=\\"markdown-toc-11单步移动内置指针\\">1.1、单步移动内置指针</a></li>\\n <li><a href=\\"#12获取-get-指针和-put-指针的位置\\" id=\\"markdown-toc-12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</a></li>\\n <li><a href=\\"#13设置-get-和-put-指针可达区域的上下界\\" id=\\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2memorystreambuf\\" id=\\"markdown-toc-2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></a> <ul>\\n <li><a href=\\"#21移动内置的-get-和-put-指针\\" id=\\"markdown-toc-21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</a></li>\\n <li><a href=\\"#22获取-get-和-put-指针当前位置\\" id=\\"markdown-toc-22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</a></li>\\n <li><a href=\\"#23获取缓冲区的起始位置和大小\\" id=\\"markdown-toc-23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</a></li>\\n <li><a href=\\"#24缓冲区的已写字节数\\" id=\\"markdown-toc-24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</a></li>\\n <li><a href=\\"#25显式设定-put-和-get-指针位置\\" id=\\"markdown-toc-25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</a></li>\\n <li><a href=\\"#26-修改缓冲区大小\\" id=\\"markdown-toc-26-修改缓冲区大小\\">2.6 修改缓冲区大小</a></li>\\n <li><a href=\\"#27构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二io-流\\" id=\\"markdown-toc-二io-流\\">二、IO 流</a> <ul>\\n <li><a href=\\"#1了解-stdios\\" id=\\"markdown-toc-1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></a></li>\\n <li><a href=\\"#2memoryios\\" id=\\"markdown-toc-2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></a> <ul>\\n <li><a href=\\"#21构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#22得到-memorystreambuf-成员的地址\\" id=\\"markdown-toc-22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</a></li>\\n <li><a href=\\"#23当前位置\\" id=\\"markdown-toc-23当前位置\\">2.3、当前位置</a></li>\\n <li><a href=\\"#24封装-memorystreambuf-成员的一些函数\\" id=\\"markdown-toc-24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</a></li>\\n <li><a href=\\"#25-缓冲区可读数据的字节数\\" id=\\"markdown-toc-25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3输入流\\" id=\\"markdown-toc-3输入流\\">3、输入流</a></li>\\n <li><a href=\\"#4输出流\\" id=\\"markdown-toc-4输出流\\">4、输出流</a> <ul>\\n <li><a href=\\"#41-构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#42-读取和设定已写字节数\\" id=\\"markdown-toc-42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</a></li>\\n <li><a href=\\"#43-当前位置\\" id=\\"markdown-toc-43-当前位置\\">4.3 当前位置</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#三局部内存片\\" id=\\"markdown-toc-三局部内存片\\">三、局部内存片</a> <ul>\\n <li><a href=\\"#1构造函数\\" id=\\"markdown-toc-1构造函数\\">1、构造函数</a></li>\\n <li><a href=\\"#2析构函数\\" id=\\"markdown-toc-2析构函数\\">2、析构函数</a></li>\\n <li><a href=\\"#3缓冲区切割\\" id=\\"markdown-toc-3缓冲区切割\\">3、缓冲区切割</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\\n\\n<h3 id=\\"一流缓冲区\\">一、流缓冲区</h3>\\n\\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\\n\\n<h4 id=\\"1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></h4>\\n\\n<p>首先要了解 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 内置了一个 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针和一个 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针。<code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\\"language-plaintext highlighter-rouge\\">g</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">p</code> 就分别表示 get pointer 和 put pointer。</p>\\n\\n<h5 id=\\"11单步移动内置指针\\">1.1、单步移动内置指针</h5>\\n\\n<p>Increase get pointer: Advances the get pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">gbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>Increase put pointer: Advances the put pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">pbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</h5>\\n\\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">gptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">pptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</h5>\\n\\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setg</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gnext</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setp</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<h4 id=\\"2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></h4>\\n\\n<p>类定义:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryStreamBuf</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">streambuf</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">friend</span> <span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span><span class=\\"p\\">;</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">overflow</span><span class=\\"p\\">(</span><span class=\\"n\\">int_type</span> <span class=\\"n\\">c</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">underflow</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">sync</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"k\\">operator</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\\n\\n<h5 id=\\"21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针都移动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">gbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</h5>\\n\\n<p>封装 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">gptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</h5>\\n\\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</h5>\\n\\n<p>读取(其中也可能发生设置操作):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// 已写字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">written</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_written</span><span class=\\"o\\">=</span><span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</h5>\\n\\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Save nb char written</span>\\n \\n <span class=\\"c1\\">// 移动 put 指针</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 移动 get 指针</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"26-修改缓冲区大小\\">2.6 修改缓冲区大小</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// 大小标识</span>\\n <span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// gptr 当前位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span> \\n <span class=\\"c1\\">// pptr 当前位置</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>构造函数会设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">bufferSize</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数会拷贝对方的 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code>、<code class=\\"language-plaintext highlighter-rouge\\">bufferSizse</code>、<code class=\\"language-plaintext highlighter-rouge\\">_written</code>,并设定 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>、<code class=\\"language-plaintext highlighter-rouge\\">pptr</code>。注意设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 时,要分别调用 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pbump</code>,因为 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 仅将 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">(),</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">));</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二io-流\\">二、IO 流</h3>\\n\\n<h4 id=\\"1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></h4>\\n\\n<p>Initialize object [<code class=\\"language-plaintext highlighter-rouge\\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">init</span> <span class=\\"p\\">(</span> <span class=\\"n\\">streambuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">sb</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>初始化后如下函数的返回值:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>member function</th>\\n <th>value</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>rdbuf()</td>\\n <td>sb</td>\\n </tr>\\n <tr>\\n <td>tie()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>rdstate()</td>\\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\\n </tr>\\n <tr>\\n <td>exceptions()</td>\\n <td>goodbit</td>\\n </tr>\\n <tr>\\n <td>flags()</td>\\n <td>skipws | dec</td>\\n </tr>\\n <tr>\\n <td>width()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>precision()</td>\\n <td>6</td>\\n </tr>\\n <tr>\\n <td>fill()</td>\\n <td>‘ ’ (whitespace)</td>\\n </tr>\\n <tr>\\n <td>getloc()</td>\\n <td>a copy of locale()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code>,且是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryIOS</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"k\\">virtual</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ios</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span> <span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">poco_ios_init</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">init</code> 的宏定义,用于初始化成员 <code class=\\"language-plaintext highlighter-rouge\\">_buf</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_buf</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23当前位置\\">2.3、当前位置</h5>\\n\\n<p>这是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutpuStream</code> 继承时实现:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code> 封装为 <code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"p\\">(</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">());</span> <span class=\\"c1\\">// 缓冲区剩余可读数据字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">result</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3输入流\\">3、输入流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryInputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryInputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for reading.</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 以及 <code class=\\"language-plaintext highlighter-rouge\\">istream</code>。<code class=\\"language-plaintext highlighter-rouge\\">istream</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">iostream</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">basic_istream</code> 别名(<code class=\\"language-plaintext highlighter-rouge\\">typedef</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"k\\">const_cast</span><span class=\\"o\\">&lt;</span><span class=\\"kt\\">char</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>唯一的一个成员函数是 <code class=\\"language-plaintext highlighter-rouge\\">current</code>,封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的 <code class=\\"language-plaintext highlighter-rouge\\">gCurrent</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4输出流\\">4、输出流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryOutputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span>\\n <span class=\\"c1\\">/// An input stream for reading from a memory area.</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryOutputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for writing.</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>如下,不赘述了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</h5>\\n\\n<p>读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"43-当前位置\\">4.3 当前位置</h5>\\n\\n<p>与 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 中的封装类似:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三局部内存片\\">三、局部内存片</h3>\\n\\n<p>在第一部分的流缓冲区介绍 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 中有一个 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"1构造函数\\">1、构造函数</h4>\\n\\n<p>构造函数传入的参数对应的就是 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">(</span><span class=\\"n\\">offset</span><span class=\\"p\\">),</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">(</span><span class=\\"n\\">buffer</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&gt;=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2析构函数\\">2、析构函数</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3缓冲区切割\\">3、缓冲区切割</h4>\\n\\n<p>可以看到构造函数和析构函数中都调用了 <code class=\\"language-plaintext highlighter-rouge\\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\\n\\n<ul>\\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 获取到 gptr</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gpos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"c1\\">// 偏移缓冲区地址,并修改缓冲区大小</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">ppos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">gpos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">ppos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\\n</ol>\\n","output":"<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T03:31:10+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 12668 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一流缓冲区\\" id=\\"markdown-toc-一流缓冲区\\">一、流缓冲区</a> <ul>\\n <li><a href=\\"#1了解-stdstreambuf\\" id=\\"markdown-toc-1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></a> <ul>\\n <li><a href=\\"#11单步移动内置指针\\" id=\\"markdown-toc-11单步移动内置指针\\">1.1、单步移动内置指针</a></li>\\n <li><a href=\\"#12获取-get-指针和-put-指针的位置\\" id=\\"markdown-toc-12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</a></li>\\n <li><a href=\\"#13设置-get-和-put-指针可达区域的上下界\\" id=\\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2memorystreambuf\\" id=\\"markdown-toc-2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></a> <ul>\\n <li><a href=\\"#21移动内置的-get-和-put-指针\\" id=\\"markdown-toc-21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</a></li>\\n <li><a href=\\"#22获取-get-和-put-指针当前位置\\" id=\\"markdown-toc-22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</a></li>\\n <li><a href=\\"#23获取缓冲区的起始位置和大小\\" id=\\"markdown-toc-23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</a></li>\\n <li><a href=\\"#24缓冲区的已写字节数\\" id=\\"markdown-toc-24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</a></li>\\n <li><a href=\\"#25显式设定-put-和-get-指针位置\\" id=\\"markdown-toc-25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</a></li>\\n <li><a href=\\"#26-修改缓冲区大小\\" id=\\"markdown-toc-26-修改缓冲区大小\\">2.6 修改缓冲区大小</a></li>\\n <li><a href=\\"#27构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二io-流\\" id=\\"markdown-toc-二io-流\\">二、IO 流</a> <ul>\\n <li><a href=\\"#1了解-stdios\\" id=\\"markdown-toc-1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></a></li>\\n <li><a href=\\"#2memoryios\\" id=\\"markdown-toc-2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></a> <ul>\\n <li><a href=\\"#21构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#22得到-memorystreambuf-成员的地址\\" id=\\"markdown-toc-22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</a></li>\\n <li><a href=\\"#23当前位置\\" id=\\"markdown-toc-23当前位置\\">2.3、当前位置</a></li>\\n <li><a href=\\"#24封装-memorystreambuf-成员的一些函数\\" id=\\"markdown-toc-24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</a></li>\\n <li><a href=\\"#25-缓冲区可读数据的字节数\\" id=\\"markdown-toc-25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3输入流\\" id=\\"markdown-toc-3输入流\\">3、输入流</a></li>\\n <li><a href=\\"#4输出流\\" id=\\"markdown-toc-4输出流\\">4、输出流</a> <ul>\\n <li><a href=\\"#41-构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#42-读取和设定已写字节数\\" id=\\"markdown-toc-42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</a></li>\\n <li><a href=\\"#43-当前位置\\" id=\\"markdown-toc-43-当前位置\\">4.3 当前位置</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#三局部内存片\\" id=\\"markdown-toc-三局部内存片\\">三、局部内存片</a> <ul>\\n <li><a href=\\"#1构造函数\\" id=\\"markdown-toc-1构造函数\\">1、构造函数</a></li>\\n <li><a href=\\"#2析构函数\\" id=\\"markdown-toc-2析构函数\\">2、析构函数</a></li>\\n <li><a href=\\"#3缓冲区切割\\" id=\\"markdown-toc-3缓冲区切割\\">3、缓冲区切割</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\\n\\n<h3 id=\\"一流缓冲区\\">一、流缓冲区</h3>\\n\\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\\n\\n<h4 id=\\"1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></h4>\\n\\n<p>首先要了解 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 内置了一个 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针和一个 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针。<code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\\"language-plaintext highlighter-rouge\\">g</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">p</code> 就分别表示 get pointer 和 put pointer。</p>\\n\\n<h5 id=\\"11单步移动内置指针\\">1.1、单步移动内置指针</h5>\\n\\n<p>Increase get pointer: Advances the get pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">gbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>Increase put pointer: Advances the put pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">pbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</h5>\\n\\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">gptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">pptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</h5>\\n\\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setg</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gnext</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setp</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<h4 id=\\"2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></h4>\\n\\n<p>类定义:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryStreamBuf</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">streambuf</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">friend</span> <span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span><span class=\\"p\\">;</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">overflow</span><span class=\\"p\\">(</span><span class=\\"n\\">int_type</span> <span class=\\"n\\">c</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">underflow</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">sync</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"k\\">operator</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\\n\\n<h5 id=\\"21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针都移动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">gbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</h5>\\n\\n<p>封装 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">gptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</h5>\\n\\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</h5>\\n\\n<p>读取(其中也可能发生设置操作):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// 已写字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">written</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_written</span><span class=\\"o\\">=</span><span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</h5>\\n\\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Save nb char written</span>\\n \\n <span class=\\"c1\\">// 移动 put 指针</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 移动 get 指针</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"26-修改缓冲区大小\\">2.6 修改缓冲区大小</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// 大小标识</span>\\n <span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// gptr 当前位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span> \\n <span class=\\"c1\\">// pptr 当前位置</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>构造函数会设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">bufferSize</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数会拷贝对方的 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code>、<code class=\\"language-plaintext highlighter-rouge\\">bufferSizse</code>、<code class=\\"language-plaintext highlighter-rouge\\">_written</code>,并设定 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>、<code class=\\"language-plaintext highlighter-rouge\\">pptr</code>。注意设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 时,要分别调用 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pbump</code>,因为 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 仅将 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">(),</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">));</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二io-流\\">二、IO 流</h3>\\n\\n<h4 id=\\"1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></h4>\\n\\n<p>Initialize object [<code class=\\"language-plaintext highlighter-rouge\\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">init</span> <span class=\\"p\\">(</span> <span class=\\"n\\">streambuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">sb</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>初始化后如下函数的返回值:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>member function</th>\\n <th>value</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>rdbuf()</td>\\n <td>sb</td>\\n </tr>\\n <tr>\\n <td>tie()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>rdstate()</td>\\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\\n </tr>\\n <tr>\\n <td>exceptions()</td>\\n <td>goodbit</td>\\n </tr>\\n <tr>\\n <td>flags()</td>\\n <td>skipws | dec</td>\\n </tr>\\n <tr>\\n <td>width()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>precision()</td>\\n <td>6</td>\\n </tr>\\n <tr>\\n <td>fill()</td>\\n <td>‘ ’ (whitespace)</td>\\n </tr>\\n <tr>\\n <td>getloc()</td>\\n <td>a copy of locale()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code>,且是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryIOS</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"k\\">virtual</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ios</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span> <span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">poco_ios_init</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">init</code> 的宏定义,用于初始化成员 <code class=\\"language-plaintext highlighter-rouge\\">_buf</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_buf</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23当前位置\\">2.3、当前位置</h5>\\n\\n<p>这是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutpuStream</code> 继承时实现:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code> 封装为 <code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"p\\">(</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">());</span> <span class=\\"c1\\">// 缓冲区剩余可读数据字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">result</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3输入流\\">3、输入流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryInputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryInputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for reading.</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 以及 <code class=\\"language-plaintext highlighter-rouge\\">istream</code>。<code class=\\"language-plaintext highlighter-rouge\\">istream</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">iostream</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">basic_istream</code> 别名(<code class=\\"language-plaintext highlighter-rouge\\">typedef</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"k\\">const_cast</span><span class=\\"o\\">&lt;</span><span class=\\"kt\\">char</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>唯一的一个成员函数是 <code class=\\"language-plaintext highlighter-rouge\\">current</code>,封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的 <code class=\\"language-plaintext highlighter-rouge\\">gCurrent</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4输出流\\">4、输出流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryOutputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span>\\n <span class=\\"c1\\">/// An input stream for reading from a memory area.</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryOutputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for writing.</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>如下,不赘述了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</h5>\\n\\n<p>读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"43-当前位置\\">4.3 当前位置</h5>\\n\\n<p>与 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 中的封装类似:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三局部内存片\\">三、局部内存片</h3>\\n\\n<p>在第一部分的流缓冲区介绍 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 中有一个 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"1构造函数\\">1、构造函数</h4>\\n\\n<p>构造函数传入的参数对应的就是 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">(</span><span class=\\"n\\">offset</span><span class=\\"p\\">),</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">(</span><span class=\\"n\\">buffer</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&gt;=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2析构函数\\">2、析构函数</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3缓冲区切割\\">3、缓冲区切割</h4>\\n\\n<p>可以看到构造函数和析构函数中都调用了 <code class=\\"language-plaintext highlighter-rouge\\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\\n\\n<ul>\\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 获取到 gptr</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gpos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"c1\\">// 偏移缓冲区地址,并修改缓冲区大小</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">ppos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">gpos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">ppos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","excerpt":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。","url":"/2012/04/24/openrtmfp-cumulus-5/","collection":"posts","relative_path":"_posts/2012-04-24-openrtmfp-cumulus-5.markdown","draft":false,"categories":["rt_tech"],"layout":"post","title":"OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析","date":"2012-04-24 03:31:10 +0000","tags":["直播技术"],"description":"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。","location":"广州","slug":"openrtmfp-cumulus-5","ext":".markdown"}],"total_posts":24,"total_pages":2,"previous_page":null,"previous_page_path":null,"next_page":2,"next_page_path":"/page2/"}\n</pre> -->\n\n<!-- Posts -->\n<ul id=\"posts\">\n\n\t\n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2022/12/24/captain-nlp-1/\">自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</a></h2>\n <time datetime=\"2022-12-24T15:08:01+00:00\" class=\"by-line\"> <i>24 Dec 2022</i> </time>\n\t \t<p>火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2022/12/17/ai-bert-1/\">你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</a></h2>\n <time datetime=\"2022-12-17T15:08:01+00:00\" class=\"by-line\"> <i>17 Dec 2022</i> </time>\n\t \t<p>2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2022/12/11/wechat-chatgpt/\">动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</a></h2>\n <time datetime=\"2022-12-11T15:59:57+00:00\" class=\"by-line\"> <i>11 Dec 2022</i> </time>\n\t \t<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作 ……</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2022/11/30/midjourney-first-test/\">确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</a></h2>\n <time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\"> <i>30 Nov 2022</i> </time>\n\t \t<p>因为 Diffusion 模型在计算机视觉领域的发展,可以说今年人工智能在计算机视觉领域大放异彩,各种 Text2Image 项目层出不穷,花了三分钟时间做了一组机甲图,确实非常惊艳 ……</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2022/08/11/captain-alibaba/\">不要船开远了,就忘了为什么启航</a></h2>\n <time datetime=\"2022-08-11T15:53:57+00:00\" class=\"by-line\"> <i>11 Aug 2022</i> </time>\n\t \t<p>2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2021/12/23/captains-jeckyll-learning/\">麦克船长的 Jekyll 快速教程</a></h2>\n <time datetime=\"2021-12-23T19:43:02+00:00\" class=\"by-line\"> <i>23 Dec 2021</i> </time>\n\t \t<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可 ……</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2021/12/21/build-github-pages-with-jekyll/\">如何使用 Jekyll 基于 Github Pages 搭建个人博客</a></h2>\n <time datetime=\"2021-12-21T15:53:57+00:00\" class=\"by-line\"> <i>21 Dec 2021</i> </time>\n\t \t<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客 ……</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2021/11/11/captain-tttm/\">欢迎成为「淘宝-天天特卖」团队的创业合伙人!</a></h2>\n <time datetime=\"2021-11-11T19:59:43+00:00\" class=\"by-line\"> <i>11 Nov 2021</i> </time>\n\t \t<p>阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2021/06/04/captain-alibaba-1st-anniversary/\">麦克船长的阿里一年香(入职阿里一周年)</a></h2>\n <time datetime=\"2021-06-04T15:42:43+00:00\" class=\"by-line\"> <i>04 Jun 2021</i> </time>\n\t \t<p>本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2020/11/11/captain-double-eleven/\">担任淘宝产品总负责人的双十一,是怎样的体验?</a></h2>\n <time datetime=\"2020-11-11T15:59:43+00:00\" class=\"by-line\"> <i>11 Nov 2020</i> </time>\n\t \t<p>本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2020/04/14/covid2019-catering-business-mode/\">疫后怎么做餐饮品牌?三叉戟模式或成标配</a></h2>\n <time datetime=\"2020-04-14T16:42:43+00:00\" class=\"by-line\"> <i>14 Apr 2020</i> </time>\n\t \t<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题 ……</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2020/04/11/delayed-gratification/\">延迟满足,才有自由</a></h2>\n <time datetime=\"2020-04-11T06:18:03+00:00\" class=\"by-line\"> <i>11 Apr 2020</i> </time>\n\t \t<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上 ……</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2017/02/23/ai-make-people-life-as-billionaires/\">未来人工智能就是要:让普通人过上现在富豪们的生活</a></h2>\n <time datetime=\"2017-02-23T18:23:33+00:00\" class=\"by-line\"> <i>23 Feb 2017</i> </time>\n\t \t<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域 ……</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2017/01/31/danshari-vs-remember-reverberations/\">我们是应该「断舍离」还是「念念不忘,必有回响」</a></h2>\n <time datetime=\"2017-01-31T20:59:21+00:00\" class=\"by-line\"> <i>31 Jan 2017</i> </time>\n\t \t<p>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」 ……</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2012/08/04/openrtmfp-cumulus-9/\">OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</a></h2>\n <time datetime=\"2012-08-04T17:58:17+00:00\" class=\"by-line\"> <i>04 Aug 2012</i> </time>\n\t \t<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2012/07/23/openrtmfp-cumulus-8/\">OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</a></h2>\n <time datetime=\"2012-07-23T03:07:43+00:00\" class=\"by-line\"> <i>23 Jul 2012</i> </time>\n\t \t<p>Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2012/06/25/openrtmfp-cumulus-7/\">OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</a></h2>\n <time datetime=\"2012-06-25T02:56:26+00:00\" class=\"by-line\"> <i>25 Jun 2012</i> </time>\n\t \t<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2012/06/07/openrtmfp-cumulus-6/\">OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</a></h2>\n <time datetime=\"2012-06-07T15:34:18+00:00\" class=\"by-line\"> <i>07 Jun 2012</i> </time>\n\t \t<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2012/05/15/excellent-software-engineer-basic-skills/\">一名出色软件工程师的技术基本功:编程与工具</a></h2>\n <time datetime=\"2012-05-15T17:06:59+00:00\" class=\"by-line\"> <i>15 May 2012</i> </time>\n\t \t<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解 ……</p>\n\t </li>\n\n \n\n\t <li class=\"post\">\n\t \t<h2><a href=\"/pages/Poechant/2012/04/24/openrtmfp-cumulus-5/\">OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</a></h2>\n <time datetime=\"2012-04-24T03:31:10+00:00\" class=\"by-line\"> <i>24 Apr 2012</i> </time>\n\t \t<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\n\t </li>\n\n \n\n</ul>\n","*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html,body,h1,h2,h3,h4,h5,h6,p,ul,ol,li,img{margin:0;padding:0;border:0}html{background-color:#fff;font-size:16px;line-height:1.8;color:#333}@media (min-width: 940px){html{font-size:18px}}body{max-width:1000px;margin:0 auto;padding:0 10px}a{color:#333;text-decoration:none;font-weight:700}a:hover,a:focus{color:#262626}.f-right{float:right}.f-left{float:left}.clear{clear:both}.parent{display:flex}.inner{align-self:center}.justify-center{justify-content:center}.justify-spaceBetween{justify-content:space-between}.w100{width:100%}.h100{height:100%}.wh100{width:100%;height:100%}.absolute{position:absolute}.relative{position:relative}.top{top:0}.bottom{bottom:0}.right{right:0}.left{left:0}body{font-family:\"Roboto\", \"Helvetica Neue\", \"Helvetica\", \"Arial\", sans-serif;font-style:normal;font-weight:400;font-size:16px;color:#333}p,ul,ol{font-size:1em;line-height:1.8em;margin-bottom:1.5em}h1{font-size:2.25em;line-height:1.8em;padding:0.33335em 0}h2{font-size:1.5em;line-height:1.8em;padding:1em 0 0 0;margin-bottom:1.5em}h3{font-size:1.125em;line-height:1.8em;padding:0.66667em 0;margin-bottom:1.5em}h4,h5,h6{font-size:1em;line-height:1.8em;padding:0.5em 0;margin-bottom:1.5em}blockquote{font-style:italic;margin:1.5em;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px;background-color:#f2f2f2;padding:0 1.5em}blockquote p,blockquote ul,blockquote ol{padding:1.5em 0}@media (min-width: 940px){p,ul,ol{font-size:1em;line-height:1.8em;margin-bottom:1.3334em}h1{font-size:2.6667em;line-height:1.8em;padding:0.25em 0}h2{font-size:2em;line-height:1.8em;padding:0.66667em 0 0 0;margin-bottom:1em}h3{font-size:1.3334em;line-height:1.8em;padding:0.5em 0;margin-bottom:1em}h4,h5,h6{font-size:1.1111em;line-height:1.8em;padding:0.5em 0;margin-bottom:1em}blockquote{font-style:italic;margin:1.3334em;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px;background-color:#f2f2f2;padding:0 1.33334em}blockquote p,blockquote ul,blockquote ol{padding:1.33334em 0}}#nav a,#nav-left a{display:block;color:#333;padding:0.33334em 0;font-size:1.5em;font-weight:400}@media (min-width: 940px){#nav a,#nav-left a{font-size:1em}}#nav a:hover,#nav-left a:hover{background-color:rgba(13,13,13,0.6)}#nav span,#nav-left span{font-weight:200}#nav{width:14rem;position:fixed;background-color:#fff;top:0;bottom:0;right:-14rem;color:#fff;opacity:0.95;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;-ms-transition:all 0.3s ease-in;transition:all 0.3s ease-in;z-index:1;padding:72px 0;text-align:center}#nav-left{width:14rem;position:fixed;background-color:#fff;top:0;bottom:0;left:-14rem;color:#fff;opacity:0.95;-webkit-transition:all 0.3s ease-in;-moz-transition:all 0.3s ease-in;-ms-transition:all 0.3s ease-in;transition:all 0.3s ease-in;z-index:1;padding:72px 0;text-align:center}#nav.menu-open{-webkit-transform:translateX(-14rem);-moz-transform:translateX(-14rem);-ms-transform:translateX(-14rem);transform:translateX(-14rem);width:100%}@media (min-width: 940px){#nav.menu-open{width:30%}}#nav-left.menu-open-left{-webkit-transform:translateX(14rem);-moz-transform:translateX(14rem);-ms-transform:translateX(14rem);transform:translateX(14rem);width:100%}@media (min-width: 940px){#nav-left.menu-open-left{width:30%}}@media (max-width: 940px){#nav-links{display:none}}#nav-links a{padding-left:10px;color:#333;font-weight:300}#nav-list:after{display:block;content:'';width:5rem;height:1px;margin:23px auto;background-color:#333}#nav-menu{display:block;position:fixed;top:35px;right:25px;z-index:10;height:24px}@media (min-width: 940px){#nav-menu{display:none}}#nav-menu-left{display:block;position:fixed;top:35px;left:25px;z-index:10;height:24px}@media (min-width: 940px){#nav-menu-left{display:none}}#menu{height:4px;width:1.5em;background-color:#333;margin-top:8px}#menu:after,#menu:before{content:\"\";display:block;position:relative;height:4px;width:1.5em;background-color:#333;transition:all 0.3s ease-in}#menu:before{top:-8px}#menu:after{top:4px}#menu.btn-close{background:none}#menu.btn-close:before{top:0;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);transform:rotate(-45deg);background-color:#333}#menu.btn-close:after{top:-4px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);background-color:#333}.fixed{position:fixed;width:100%}@media (min-width: 940px){.fixed{position:static}}#container{margin:0 auto}#header{border-bottom:1px solid rgba(0,0,0,0.14);text-align:center;margin-bottom:3em;height:5em;position:relative}#header a{text-decoration:none;display:inline-block}#header div{margin:0 auto}#header h1{font-size:2em;padding-bottom:0}#header h1 span{color:rgba(0,0,0,0.6);font-weight:300}#posts li{list-style-type:none}#post-page{margin-bottom:1.5em}@media (min-width: 940px){#post-page{margin-bottom:1.3334em}}.post+.post:before{display:block;content:'';width:5rem;height:1px;margin:23px auto;background-color:#e6e6e6}.by-line{display:block;color:#737373;line-height:1.8em;margin-bottom:1.5em;font-weight:200}@media (min-width: 940px){.by-line{display:block;color:#737373;line-height:1.8em;margin-bottom:1.3334em;font-weight:200}}img{max-width:100%;display:block;margin:0 auto;margin-bottom:24px;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px}img[title=\"Monochrome\"]{box-shadow:0 2px 6px #ddd}code{color:#8c8c8c;background-color:#fff}.content ul,.content ol{line-height:1.8em;padding-left:1.5em}@media (min-width: 940px){.content ul,.content ol{line-height:1.8em}}#page ul,#page ol{padding-left:1.5em}.pagination{text-align:center;margin:2.666668em}.pagination span{background-color:#f2f2f2;color:#333}.pagination a:hover{background-color:#404040}.page-item{background-color:#4d4d4d;color:#fff;padding:4px 8px;font-weight:400;padding:0.5em 1em;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px}footer{background-color:#fff;color:#333;text-align:center;padding:5.6667em 0}.lineno{color:#bfbfbf;margin-right:1em}.highlight .c{color:#999988;font-style:italic}.highlight .err{color:#a61717;background-color:#e3d2d2}.highlight .k{font-weight:bold}.highlight .o{font-weight:bold}.highlight .cm{color:#999988;font-style:italic}.highlight .cp{color:#999999;font-weight:bold}.highlight .c1{color:#999988;font-style:italic}.highlight .cs{color:#999999;font-weight:bold;font-style:italic}.highlight .gd{color:#000000;background-color:#fdd}.highlight .gd .x{color:#000000;background-color:#faa}.highlight .ge{font-style:italic}.highlight .gr{color:#a00}.highlight .gh{color:#999}.highlight .gi{color:#000000;background-color:#dfd}.highlight .gi .x{color:#000000;background-color:#afa}.highlight .go{color:#888}.highlight .gp{color:#555}.highlight .gs{font-weight:bold}.highlight .gu{color:#aaa}.highlight .gt{color:#a00}.highlight .kc{font-weight:bold}.highlight .kd{font-weight:bold}.highlight .kp{font-weight:bold}.highlight .kr{font-weight:bold}.highlight .kt{color:#445588;font-weight:bold}.highlight .m{color:#099}.highlight .s{color:#d14}.highlight .na{color:teal}.highlight .nb{color:#0086B3}.highlight .nc{color:#445588;font-weight:bold}.highlight .no{color:teal}.highlight .ni{color:purple}.highlight .ne{color:#990000;font-weight:bold}.highlight .nf{color:#990000;font-weight:bold}.highlight .nn{color:#555}.highlight .nt{color:navy}.highlight .nv{color:teal}.highlight .ow{font-weight:bold}.highlight .w{color:#bbb}.highlight .mf{color:#099}.highlight .mh{color:#099}.highlight .mi{color:#099}.highlight .mo{color:#099}.highlight .sb{color:#d14}.highlight .sc{color:#d14}.highlight .sd{color:#d14}.highlight .s2{color:#d14}.highlight .se{color:#d14}.highlight .sh{color:#d14}.highlight .si{color:#d14}.highlight .sx{color:#d14}.highlight .sr{color:#009926}.highlight .s1{color:#d14}.highlight .ss{color:#990073}.highlight .bp{color:#999}.highlight .vc{color:teal}.highlight .vg{color:teal}.highlight .vi{color:teal}.highlight .il{color:#099}pre{background-color:#f9f9f9;border:1px solid #e8e8e8;padding:1em}pre code{background-color:#f9f9f9;border:none}code{background-color:#f9f9f9;border:1px solid #e8e8e8}#markdown-toc{background-color:#f9f9f9;font-size:0.7em;font-style:normal}p a{color:#1e6afe}p a:hover,p a:focus{color:#6297ff}\n","","","","/*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}template,[hidden]{display:none !important}a{background-color:transparent}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}button,input,select,textarea{font:inherit;margin:0}optgroup{font-weight:bold}button,input{overflow:visible}button,select{text-transform:none}button,html [type=\"button\"],[type=\"reset\"],[type=\"submit\"]{-webkit-appearance:button}button::-moz-focus-inner,[type=\"button\"]::-moz-focus-inner,[type=\"reset\"]::-moz-focus-inner,[type=\"submit\"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=\"button\"]:-moz-focusring,[type=\"reset\"]:-moz-focusring,[type=\"submit\"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type=\"checkbox\"],[type=\"radio\"]{box-sizing:border-box;padding:0}[type=\"number\"]::-webkit-inner-spin-button,[type=\"number\"]::-webkit-outer-spin-button{height:auto}[type=\"search\"]{-webkit-appearance:textfield;outline-offset:-2px}[type=\"search\"]::-webkit-search-cancel-button,[type=\"search\"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:0.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}*{box-sizing:border-box}input,select,textarea,button{font-family:inherit;font-size:inherit;line-height:inherit}body{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\";font-size:14px;line-height:1.5;color:#24292e;background-color:#fff}a{color:#0366d6;text-decoration:none}a:hover{text-decoration:underline}b,strong{font-weight:600}hr,.rule{height:0;margin:15px 0;overflow:hidden;background:transparent;border:0;border-bottom:1px solid #dfe2e5}hr::before,.rule::before{display:table;content:\"\"}hr::after,.rule::after{display:table;clear:both;content:\"\"}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}button{cursor:pointer;border-radius:0}[hidden][hidden]{display:none !important}details summary{cursor:pointer}details:not([open])>*:not(summary){display:none !important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:0}h1{font-size:32px;font-weight:600}h2{font-size:24px;font-weight:600}h3{font-size:20px;font-weight:600}h4{font-size:16px;font-weight:600}h5{font-size:14px;font-weight:600}h6{font-size:12px;font-weight:600}p{margin-top:0;margin-bottom:10px}small{font-size:90%}blockquote{margin:0}ul,ol{padding-left:0;margin-top:0;margin-bottom:0}ol ol,ul ol{list-style-type:lower-roman}ul ul ol,ul ol ol,ol ul ol,ol ol ol{list-style-type:lower-alpha}dd{margin-left:0}tt,code{font-family:\"SFMono-Regular\",Consolas,\"Liberation Mono\",Menlo,Courier,monospace;font-size:12px}pre{margin-top:0;margin-bottom:0;font-family:\"SFMono-Regular\",Consolas,\"Liberation Mono\",Menlo,Courier,monospace;font-size:12px}.octicon{vertical-align:text-bottom}.anim-fade-in{animation-name:fade-in;animation-duration:1s;animation-timing-function:ease-in-out}.anim-fade-in.fast{animation-duration:300ms}@keyframes fade-in{0%{opacity:0}100%{opacity:1}}.anim-fade-out{animation-name:fade-out;animation-duration:1s;animation-timing-function:ease-out}.anim-fade-out.fast{animation-duration:0.3s}@keyframes fade-out{0%{opacity:1}100%{opacity:0}}.anim-fade-up{opacity:0;animation-name:fade-up;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease-out;animation-delay:1s}@keyframes fade-up{0%{opacity:0.8;transform:translateY(100%)}100%{opacity:1;transform:translateY(0)}}.anim-fade-down{animation-name:fade-down;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease-in}@keyframes fade-down{0%{opacity:1;transform:translateY(0)}100%{opacity:0.5;transform:translateY(100%)}}.anim-grow-x{width:0%;animation-name:grow-x;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease;animation-delay:0.5s}@keyframes grow-x{to{width:100%}}.anim-shrink-x{animation-name:shrink-x;animation-duration:0.3s;animation-fill-mode:forwards;animation-timing-function:ease-in-out;animation-delay:0.5s}@keyframes shrink-x{to{width:0%}}.anim-scale-in{animation-name:scale-in;animation-duration:0.15s;animation-timing-function:cubic-bezier(0.2, 0, 0.13, 1.5)}@keyframes scale-in{0%{opacity:0;transform:scale(0.5)}100%{opacity:1;transform:scale(1)}}.anim-pulse{animation-name:pulse;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}@keyframes pulse{0%{opacity:0.3}10%{opacity:1}100%{opacity:0.3}}.anim-pulse-in{animation-name:pulse-in;animation-duration:0.5s}@keyframes pulse-in{0%{transform:scale3d(1, 1, 1)}50%{transform:scale3d(1.1, 1.1, 1.1)}100%{transform:scale3d(1, 1, 1)}}.hover-grow{transition:transform 0.3s;backface-visibility:hidden}.hover-grow:hover{transform:scale(1.025)}.border{border:1px #e1e4e8 solid !important}.border-y{border-top:1px #e1e4e8 solid !important;border-bottom:1px #e1e4e8 solid !important}.border-0{border:0 !important}.border-dashed{border-style:dashed !important}.border-blue{border-color:#0366d6 !important}.border-blue-light{border-color:#c8e1ff !important}.border-green{border-color:#34d058 !important}.border-green-light{border-color:#a2cbac !important}.border-red{border-color:#d73a49 !important}.border-red-light{border-color:#cea0a5 !important}.border-purple{border-color:#6f42c1 !important}.border-yellow{border-color:#d9d0a5 !important}.border-gray-light{border-color:#eaecef !important}.border-gray-dark{border-color:#d1d5da !important}.border-black-fade{border-color:rgba(27,31,35,0.15) !important}.border-top{border-top:1px #e1e4e8 solid !important}.border-right{border-right:1px #e1e4e8 solid !important}.border-bottom{border-bottom:1px #e1e4e8 solid !important}.border-left{border-left:1px #e1e4e8 solid !important}.border-top-0{border-top:0 !important}.border-right-0{border-right:0 !important}.border-bottom-0{border-bottom:0 !important}.border-left-0{border-left:0 !important}.rounded-0{border-radius:0 !important}.rounded-1{border-radius:3px !important}.rounded-2{border-radius:6px !important}.rounded-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}@media (min-width: 544px){.border-sm-top{border-top:1px #e1e4e8 solid !important}.border-sm-right{border-right:1px #e1e4e8 solid !important}.border-sm-bottom{border-bottom:1px #e1e4e8 solid !important}.border-sm-left{border-left:1px #e1e4e8 solid !important}.border-sm-top-0{border-top:0 !important}.border-sm-right-0{border-right:0 !important}.border-sm-bottom-0{border-bottom:0 !important}.border-sm-left-0{border-left:0 !important}.rounded-sm-0{border-radius:0 !important}.rounded-sm-1{border-radius:3px !important}.rounded-sm-2{border-radius:6px !important}.rounded-sm-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-sm-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-sm-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-sm-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-sm-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-sm-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-sm-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-sm-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-sm-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-sm-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-sm-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-sm-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}@media (min-width: 768px){.border-md-top{border-top:1px #e1e4e8 solid !important}.border-md-right{border-right:1px #e1e4e8 solid !important}.border-md-bottom{border-bottom:1px #e1e4e8 solid !important}.border-md-left{border-left:1px #e1e4e8 solid !important}.border-md-top-0{border-top:0 !important}.border-md-right-0{border-right:0 !important}.border-md-bottom-0{border-bottom:0 !important}.border-md-left-0{border-left:0 !important}.rounded-md-0{border-radius:0 !important}.rounded-md-1{border-radius:3px !important}.rounded-md-2{border-radius:6px !important}.rounded-md-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-md-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-md-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-md-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-md-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-md-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-md-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-md-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-md-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-md-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-md-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-md-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}@media (min-width: 1012px){.border-lg-top{border-top:1px #e1e4e8 solid !important}.border-lg-right{border-right:1px #e1e4e8 solid !important}.border-lg-bottom{border-bottom:1px #e1e4e8 solid !important}.border-lg-left{border-left:1px #e1e4e8 solid !important}.border-lg-top-0{border-top:0 !important}.border-lg-right-0{border-right:0 !important}.border-lg-bottom-0{border-bottom:0 !important}.border-lg-left-0{border-left:0 !important}.rounded-lg-0{border-radius:0 !important}.rounded-lg-1{border-radius:3px !important}.rounded-lg-2{border-radius:6px !important}.rounded-lg-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-lg-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-lg-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-lg-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-lg-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-lg-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-lg-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-lg-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-lg-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-lg-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-lg-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-lg-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}@media (min-width: 1280px){.border-xl-top{border-top:1px #e1e4e8 solid !important}.border-xl-right{border-right:1px #e1e4e8 solid !important}.border-xl-bottom{border-bottom:1px #e1e4e8 solid !important}.border-xl-left{border-left:1px #e1e4e8 solid !important}.border-xl-top-0{border-top:0 !important}.border-xl-right-0{border-right:0 !important}.border-xl-bottom-0{border-bottom:0 !important}.border-xl-left-0{border-left:0 !important}.rounded-xl-0{border-radius:0 !important}.rounded-xl-1{border-radius:3px !important}.rounded-xl-2{border-radius:6px !important}.rounded-xl-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-xl-top-1{border-top-left-radius:3px !important;border-top-right-radius:3px !important}.rounded-xl-top-2{border-top-left-radius:6px !important;border-top-right-radius:6px !important}.rounded-xl-right-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-xl-right-1{border-top-right-radius:3px !important;border-bottom-right-radius:3px !important}.rounded-xl-right-2{border-top-right-radius:6px !important;border-bottom-right-radius:6px !important}.rounded-xl-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-xl-bottom-1{border-bottom-right-radius:3px !important;border-bottom-left-radius:3px !important}.rounded-xl-bottom-2{border-bottom-right-radius:6px !important;border-bottom-left-radius:6px !important}.rounded-xl-left-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-xl-left-1{border-bottom-left-radius:3px !important;border-top-left-radius:3px !important}.rounded-xl-left-2{border-bottom-left-radius:6px !important;border-top-left-radius:6px !important}}.circle{border-radius:50% !important}.box-shadow{box-shadow:0 1px 1px rgba(27,31,35,0.1) !important}.box-shadow-medium{box-shadow:0 1px 5px rgba(27,31,35,0.15) !important}.box-shadow-large{box-shadow:0 1px 15px rgba(27,31,35,0.15) !important}.box-shadow-extra-large{box-shadow:0 10px 50px rgba(27,31,35,0.07) !important}.box-shadow-none{box-shadow:none !important}.bg-white{background-color:#fff !important}.bg-blue{background-color:#0366d6 !important}.bg-blue-light{background-color:#f1f8ff !important}.bg-gray-dark{background-color:#24292e !important}.bg-gray{background-color:#f6f8fa !important}.bg-gray-light{background-color:#fafbfc !important}.bg-green{background-color:#28a745 !important}.bg-green-light{background-color:#dcffe4 !important}.bg-red{background-color:#d73a49 !important}.bg-red-light{background-color:#ffdce0 !important}.bg-yellow{background-color:#ffd33d !important}.bg-yellow-light{background-color:#fff5b1 !important}.bg-purple{background-color:#6f42c1 !important}.bg-purple-light{background-color:#f5f0ff !important}.bg-shade-gradient{background-image:linear-gradient(180deg, rgba(27,31,35,0.065), rgba(27,31,35,0)) !important;background-repeat:no-repeat !important;background-size:100% 200px !important}.text-blue{color:#0366d6 !important}.text-red{color:#cb2431 !important}.text-gray-light{color:#6a737d !important}.text-gray{color:#586069 !important}.text-gray-dark{color:#24292e !important}.text-green{color:#28a745 !important}.text-orange{color:#a04100 !important}.text-orange-light{color:#e36209 !important}.text-purple{color:#6f42c1 !important}.text-white{color:#fff !important}.text-inherit{color:inherit !important}.text-pending{color:#b08800 !important}.bg-pending{color:#dbab09 !important}.link-gray{color:#586069 !important}.link-gray:hover{color:#0366d6 !important}.link-gray-dark{color:#24292e !important}.link-gray-dark:hover{color:#0366d6 !important}.link-hover-blue:hover{color:#0366d6 !important}.muted-link{color:#586069 !important}.muted-link:hover{color:#0366d6 !important;text-decoration:none}.details-overlay[open]>summary::before{position:fixed;top:0;right:0;bottom:0;left:0;z-index:80;display:block;cursor:default;content:\" \";background:transparent}.details-overlay-dark[open]>summary::before{z-index:99;background:rgba(27,31,35,0.5)}.flex-row{flex-direction:row !important}.flex-row-reverse{flex-direction:row-reverse !important}.flex-column{flex-direction:column !important}.flex-wrap{flex-wrap:wrap !important}.flex-nowrap{flex-wrap:nowrap !important}.flex-justify-start{justify-content:flex-start !important}.flex-justify-end{justify-content:flex-end !important}.flex-justify-center{justify-content:center !important}.flex-justify-between{justify-content:space-between !important}.flex-justify-around{justify-content:space-around !important}.flex-items-start{align-items:flex-start !important}.flex-items-end{align-items:flex-end !important}.flex-items-center{align-items:center !important}.flex-items-baseline{align-items:baseline !important}.flex-items-stretch{align-items:stretch !important}.flex-content-start{align-content:flex-start !important}.flex-content-end{align-content:flex-end !important}.flex-content-center{align-content:center !important}.flex-content-between{align-content:space-between !important}.flex-content-around{align-content:space-around !important}.flex-content-stretch{align-content:stretch !important}.flex-auto{flex:1 1 auto !important}.flex-shrink-0{flex-shrink:0 !important}.flex-self-auto{align-self:auto !important}.flex-self-start{align-self:flex-start !important}.flex-self-end{align-self:flex-end !important}.flex-self-center{align-self:center !important}.flex-self-baseline{align-self:baseline !important}.flex-self-stretch{align-self:stretch !important}.flex-item-equal{flex-grow:1;flex-basis:0}@media (min-width: 544px){.flex-sm-row{flex-direction:row !important}.flex-sm-row-reverse{flex-direction:row-reverse !important}.flex-sm-column{flex-direction:column !important}.flex-sm-wrap{flex-wrap:wrap !important}.flex-sm-nowrap{flex-wrap:nowrap !important}.flex-sm-justify-start{justify-content:flex-start !important}.flex-sm-justify-end{justify-content:flex-end !important}.flex-sm-justify-center{justify-content:center !important}.flex-sm-justify-between{justify-content:space-between !important}.flex-sm-justify-around{justify-content:space-around !important}.flex-sm-items-start{align-items:flex-start !important}.flex-sm-items-end{align-items:flex-end !important}.flex-sm-items-center{align-items:center !important}.flex-sm-items-baseline{align-items:baseline !important}.flex-sm-items-stretch{align-items:stretch !important}.flex-sm-content-start{align-content:flex-start !important}.flex-sm-content-end{align-content:flex-end !important}.flex-sm-content-center{align-content:center !important}.flex-sm-content-between{align-content:space-between !important}.flex-sm-content-around{align-content:space-around !important}.flex-sm-content-stretch{align-content:stretch !important}.flex-sm-auto{flex:1 1 auto !important}.flex-sm-shrink-0{flex-shrink:0 !important}.flex-sm-self-auto{align-self:auto !important}.flex-sm-self-start{align-self:flex-start !important}.flex-sm-self-end{align-self:flex-end !important}.flex-sm-self-center{align-self:center !important}.flex-sm-self-baseline{align-self:baseline !important}.flex-sm-self-stretch{align-self:stretch !important}.flex-sm-item-equal{flex-grow:1;flex-basis:0}}@media (min-width: 768px){.flex-md-row{flex-direction:row !important}.flex-md-row-reverse{flex-direction:row-reverse !important}.flex-md-column{flex-direction:column !important}.flex-md-wrap{flex-wrap:wrap !important}.flex-md-nowrap{flex-wrap:nowrap !important}.flex-md-justify-start{justify-content:flex-start !important}.flex-md-justify-end{justify-content:flex-end !important}.flex-md-justify-center{justify-content:center !important}.flex-md-justify-between{justify-content:space-between !important}.flex-md-justify-around{justify-content:space-around !important}.flex-md-items-start{align-items:flex-start !important}.flex-md-items-end{align-items:flex-end !important}.flex-md-items-center{align-items:center !important}.flex-md-items-baseline{align-items:baseline !important}.flex-md-items-stretch{align-items:stretch !important}.flex-md-content-start{align-content:flex-start !important}.flex-md-content-end{align-content:flex-end !important}.flex-md-content-center{align-content:center !important}.flex-md-content-between{align-content:space-between !important}.flex-md-content-around{align-content:space-around !important}.flex-md-content-stretch{align-content:stretch !important}.flex-md-auto{flex:1 1 auto !important}.flex-md-shrink-0{flex-shrink:0 !important}.flex-md-self-auto{align-self:auto !important}.flex-md-self-start{align-self:flex-start !important}.flex-md-self-end{align-self:flex-end !important}.flex-md-self-center{align-self:center !important}.flex-md-self-baseline{align-self:baseline !important}.flex-md-self-stretch{align-self:stretch !important}.flex-md-item-equal{flex-grow:1;flex-basis:0}}@media (min-width: 1012px){.flex-lg-row{flex-direction:row !important}.flex-lg-row-reverse{flex-direction:row-reverse !important}.flex-lg-column{flex-direction:column !important}.flex-lg-wrap{flex-wrap:wrap !important}.flex-lg-nowrap{flex-wrap:nowrap !important}.flex-lg-justify-start{justify-content:flex-start !important}.flex-lg-justify-end{justify-content:flex-end !important}.flex-lg-justify-center{justify-content:center !important}.flex-lg-justify-between{justify-content:space-between !important}.flex-lg-justify-around{justify-content:space-around !important}.flex-lg-items-start{align-items:flex-start !important}.flex-lg-items-end{align-items:flex-end !important}.flex-lg-items-center{align-items:center !important}.flex-lg-items-baseline{align-items:baseline !important}.flex-lg-items-stretch{align-items:stretch !important}.flex-lg-content-start{align-content:flex-start !important}.flex-lg-content-end{align-content:flex-end !important}.flex-lg-content-center{align-content:center !important}.flex-lg-content-between{align-content:space-between !important}.flex-lg-content-around{align-content:space-around !important}.flex-lg-content-stretch{align-content:stretch !important}.flex-lg-auto{flex:1 1 auto !important}.flex-lg-shrink-0{flex-shrink:0 !important}.flex-lg-self-auto{align-self:auto !important}.flex-lg-self-start{align-self:flex-start !important}.flex-lg-self-end{align-self:flex-end !important}.flex-lg-self-center{align-self:center !important}.flex-lg-self-baseline{align-self:baseline !important}.flex-lg-self-stretch{align-self:stretch !important}.flex-lg-item-equal{flex-grow:1;flex-basis:0}}@media (min-width: 1280px){.flex-xl-row{flex-direction:row !important}.flex-xl-row-reverse{flex-direction:row-reverse !important}.flex-xl-column{flex-direction:column !important}.flex-xl-wrap{flex-wrap:wrap !important}.flex-xl-nowrap{flex-wrap:nowrap !important}.flex-xl-justify-start{justify-content:flex-start !important}.flex-xl-justify-end{justify-content:flex-end !important}.flex-xl-justify-center{justify-content:center !important}.flex-xl-justify-between{justify-content:space-between !important}.flex-xl-justify-around{justify-content:space-around !important}.flex-xl-items-start{align-items:flex-start !important}.flex-xl-items-end{align-items:flex-end !important}.flex-xl-items-center{align-items:center !important}.flex-xl-items-baseline{align-items:baseline !important}.flex-xl-items-stretch{align-items:stretch !important}.flex-xl-content-start{align-content:flex-start !important}.flex-xl-content-end{align-content:flex-end !important}.flex-xl-content-center{align-content:center !important}.flex-xl-content-between{align-content:space-between !important}.flex-xl-content-around{align-content:space-around !important}.flex-xl-content-stretch{align-content:stretch !important}.flex-xl-auto{flex:1 1 auto !important}.flex-xl-shrink-0{flex-shrink:0 !important}.flex-xl-self-auto{align-self:auto !important}.flex-xl-self-start{align-self:flex-start !important}.flex-xl-self-end{align-self:flex-end !important}.flex-xl-self-center{align-self:center !important}.flex-xl-self-baseline{align-self:baseline !important}.flex-xl-self-stretch{align-self:stretch !important}.flex-xl-item-equal{flex-grow:1;flex-basis:0}}.position-static{position:static !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.top-0{top:0 !important}.right-0{right:0 !important}.bottom-0{bottom:0 !important}.left-0{left:0 !important}.v-align-middle{vertical-align:middle !important}.v-align-top{vertical-align:top !important}.v-align-bottom{vertical-align:bottom !important}.v-align-text-top{vertical-align:text-top !important}.v-align-text-bottom{vertical-align:text-bottom !important}.v-align-baseline{vertical-align:baseline !important}.overflow-hidden{overflow:hidden !important}.overflow-scroll{overflow:scroll !important}.overflow-auto{overflow:auto !important}.clearfix::before{display:table;content:\"\"}.clearfix::after{display:table;clear:both;content:\"\"}.float-left{float:left !important}.float-right{float:right !important}.float-none{float:none !important}@media (min-width: 544px){.float-sm-left{float:left !important}.float-sm-right{float:right !important}.float-sm-none{float:none !important}}@media (min-width: 768px){.float-md-left{float:left !important}.float-md-right{float:right !important}.float-md-none{float:none !important}}@media (min-width: 1012px){.float-lg-left{float:left !important}.float-lg-right{float:right !important}.float-lg-none{float:none !important}}@media (min-width: 1280px){.float-xl-left{float:left !important}.float-xl-right{float:right !important}.float-xl-none{float:none !important}}.width-fit{max-width:100% !important}.width-full{width:100% !important}.height-fit{max-height:100% !important}.height-full{height:100% !important}.min-width-0{min-width:0 !important}.direction-rtl{direction:rtl !important}.direction-ltr{direction:ltr !important}@media (min-width: 544px){.direction-sm-rtl{direction:rtl !important}.direction-sm-ltr{direction:ltr !important}}@media (min-width: 768px){.direction-md-rtl{direction:rtl !important}.direction-md-ltr{direction:ltr !important}}@media (min-width: 1012px){.direction-lg-rtl{direction:rtl !important}.direction-lg-ltr{direction:ltr !important}}@media (min-width: 1280px){.direction-xl-rtl{direction:rtl !important}.direction-xl-ltr{direction:ltr !important}}.m-0{margin:0 !important}.mt-0{margin-top:0 !important}.mr-0{margin-right:0 !important}.mb-0{margin-bottom:0 !important}.ml-0{margin-left:0 !important}.mx-0{margin-right:0 !important;margin-left:0 !important}.my-0{margin-top:0 !important;margin-bottom:0 !important}.m-1{margin:4px !important}.mt-1{margin-top:4px !important}.mr-1{margin-right:4px !important}.mb-1{margin-bottom:4px !important}.ml-1{margin-left:4px !important}.mt-n1{margin-top:-4px !important}.mr-n1{margin-right:-4px !important}.mb-n1{margin-bottom:-4px !important}.ml-n1{margin-left:-4px !important}.mx-1{margin-right:4px !important;margin-left:4px !important}.my-1{margin-top:4px !important;margin-bottom:4px !important}.m-2{margin:8px !important}.mt-2{margin-top:8px !important}.mr-2{margin-right:8px !important}.mb-2{margin-bottom:8px !important}.ml-2{margin-left:8px !important}.mt-n2{margin-top:-8px !important}.mr-n2{margin-right:-8px !important}.mb-n2{margin-bottom:-8px !important}.ml-n2{margin-left:-8px !important}.mx-2{margin-right:8px !important;margin-left:8px !important}.my-2{margin-top:8px !important;margin-bottom:8px !important}.m-3{margin:16px !important}.mt-3{margin-top:16px !important}.mr-3{margin-right:16px !important}.mb-3{margin-bottom:16px !important}.ml-3{margin-left:16px !important}.mt-n3{margin-top:-16px !important}.mr-n3{margin-right:-16px !important}.mb-n3{margin-bottom:-16px !important}.ml-n3{margin-left:-16px !important}.mx-3{margin-right:16px !important;margin-left:16px !important}.my-3{margin-top:16px !important;margin-bottom:16px !important}.m-4{margin:24px !important}.mt-4{margin-top:24px !important}.mr-4{margin-right:24px !important}.mb-4{margin-bottom:24px !important}.ml-4{margin-left:24px !important}.mt-n4{margin-top:-24px !important}.mr-n4{margin-right:-24px !important}.mb-n4{margin-bottom:-24px !important}.ml-n4{margin-left:-24px !important}.mx-4{margin-right:24px !important;margin-left:24px !important}.my-4{margin-top:24px !important;margin-bottom:24px !important}.m-5{margin:32px !important}.mt-5{margin-top:32px !important}.mr-5{margin-right:32px !important}.mb-5{margin-bottom:32px !important}.ml-5{margin-left:32px !important}.mt-n5{margin-top:-32px !important}.mr-n5{margin-right:-32px !important}.mb-n5{margin-bottom:-32px !important}.ml-n5{margin-left:-32px !important}.mx-5{margin-right:32px !important;margin-left:32px !important}.my-5{margin-top:32px !important;margin-bottom:32px !important}.m-6{margin:40px !important}.mt-6{margin-top:40px !important}.mr-6{margin-right:40px !important}.mb-6{margin-bottom:40px !important}.ml-6{margin-left:40px !important}.mt-n6{margin-top:-40px !important}.mr-n6{margin-right:-40px !important}.mb-n6{margin-bottom:-40px !important}.ml-n6{margin-left:-40px !important}.mx-6{margin-right:40px !important;margin-left:40px !important}.my-6{margin-top:40px !important;margin-bottom:40px !important}.mx-auto{margin-right:auto !important;margin-left:auto !important}@media (min-width: 544px){.m-sm-0{margin:0 !important}.mt-sm-0{margin-top:0 !important}.mr-sm-0{margin-right:0 !important}.mb-sm-0{margin-bottom:0 !important}.ml-sm-0{margin-left:0 !important}.mx-sm-0{margin-right:0 !important;margin-left:0 !important}.my-sm-0{margin-top:0 !important;margin-bottom:0 !important}.m-sm-1{margin:4px !important}.mt-sm-1{margin-top:4px !important}.mr-sm-1{margin-right:4px !important}.mb-sm-1{margin-bottom:4px !important}.ml-sm-1{margin-left:4px !important}.mt-sm-n1{margin-top:-4px !important}.mr-sm-n1{margin-right:-4px !important}.mb-sm-n1{margin-bottom:-4px !important}.ml-sm-n1{margin-left:-4px !important}.mx-sm-1{margin-right:4px !important;margin-left:4px !important}.my-sm-1{margin-top:4px !important;margin-bottom:4px !important}.m-sm-2{margin:8px !important}.mt-sm-2{margin-top:8px !important}.mr-sm-2{margin-right:8px !important}.mb-sm-2{margin-bottom:8px !important}.ml-sm-2{margin-left:8px !important}.mt-sm-n2{margin-top:-8px !important}.mr-sm-n2{margin-right:-8px !important}.mb-sm-n2{margin-bottom:-8px !important}.ml-sm-n2{margin-left:-8px !important}.mx-sm-2{margin-right:8px !important;margin-left:8px !important}.my-sm-2{margin-top:8px !important;margin-bottom:8px !important}.m-sm-3{margin:16px !important}.mt-sm-3{margin-top:16px !important}.mr-sm-3{margin-right:16px !important}.mb-sm-3{margin-bottom:16px !important}.ml-sm-3{margin-left:16px !important}.mt-sm-n3{margin-top:-16px !important}.mr-sm-n3{margin-right:-16px !important}.mb-sm-n3{margin-bottom:-16px !important}.ml-sm-n3{margin-left:-16px !important}.mx-sm-3{margin-right:16px !important;margin-left:16px !important}.my-sm-3{margin-top:16px !important;margin-bottom:16px !important}.m-sm-4{margin:24px !important}.mt-sm-4{margin-top:24px !important}.mr-sm-4{margin-right:24px !important}.mb-sm-4{margin-bottom:24px !important}.ml-sm-4{margin-left:24px !important}.mt-sm-n4{margin-top:-24px !important}.mr-sm-n4{margin-right:-24px !important}.mb-sm-n4{margin-bottom:-24px !important}.ml-sm-n4{margin-left:-24px !important}.mx-sm-4{margin-right:24px !important;margin-left:24px !important}.my-sm-4{margin-top:24px !important;margin-bottom:24px !important}.m-sm-5{margin:32px !important}.mt-sm-5{margin-top:32px !important}.mr-sm-5{margin-right:32px !important}.mb-sm-5{margin-bottom:32px !important}.ml-sm-5{margin-left:32px !important}.mt-sm-n5{margin-top:-32px !important}.mr-sm-n5{margin-right:-32px !important}.mb-sm-n5{margin-bottom:-32px !important}.ml-sm-n5{margin-left:-32px !important}.mx-sm-5{margin-right:32px !important;margin-left:32px !important}.my-sm-5{margin-top:32px !important;margin-bottom:32px !important}.m-sm-6{margin:40px !important}.mt-sm-6{margin-top:40px !important}.mr-sm-6{margin-right:40px !important}.mb-sm-6{margin-bottom:40px !important}.ml-sm-6{margin-left:40px !important}.mt-sm-n6{margin-top:-40px !important}.mr-sm-n6{margin-right:-40px !important}.mb-sm-n6{margin-bottom:-40px !important}.ml-sm-n6{margin-left:-40px !important}.mx-sm-6{margin-right:40px !important;margin-left:40px !important}.my-sm-6{margin-top:40px !important;margin-bottom:40px !important}.mx-sm-auto{margin-right:auto !important;margin-left:auto !important}}@media (min-width: 768px){.m-md-0{margin:0 !important}.mt-md-0{margin-top:0 !important}.mr-md-0{margin-right:0 !important}.mb-md-0{margin-bottom:0 !important}.ml-md-0{margin-left:0 !important}.mx-md-0{margin-right:0 !important;margin-left:0 !important}.my-md-0{margin-top:0 !important;margin-bottom:0 !important}.m-md-1{margin:4px !important}.mt-md-1{margin-top:4px !important}.mr-md-1{margin-right:4px !important}.mb-md-1{margin-bottom:4px !important}.ml-md-1{margin-left:4px !important}.mt-md-n1{margin-top:-4px !important}.mr-md-n1{margin-right:-4px !important}.mb-md-n1{margin-bottom:-4px !important}.ml-md-n1{margin-left:-4px !important}.mx-md-1{margin-right:4px !important;margin-left:4px !important}.my-md-1{margin-top:4px !important;margin-bottom:4px !important}.m-md-2{margin:8px !important}.mt-md-2{margin-top:8px !important}.mr-md-2{margin-right:8px !important}.mb-md-2{margin-bottom:8px !important}.ml-md-2{margin-left:8px !important}.mt-md-n2{margin-top:-8px !important}.mr-md-n2{margin-right:-8px !important}.mb-md-n2{margin-bottom:-8px !important}.ml-md-n2{margin-left:-8px !important}.mx-md-2{margin-right:8px !important;margin-left:8px !important}.my-md-2{margin-top:8px !important;margin-bottom:8px !important}.m-md-3{margin:16px !important}.mt-md-3{margin-top:16px !important}.mr-md-3{margin-right:16px !important}.mb-md-3{margin-bottom:16px !important}.ml-md-3{margin-left:16px !important}.mt-md-n3{margin-top:-16px !important}.mr-md-n3{margin-right:-16px !important}.mb-md-n3{margin-bottom:-16px !important}.ml-md-n3{margin-left:-16px !important}.mx-md-3{margin-right:16px !important;margin-left:16px !important}.my-md-3{margin-top:16px !important;margin-bottom:16px !important}.m-md-4{margin:24px !important}.mt-md-4{margin-top:24px !important}.mr-md-4{margin-right:24px !important}.mb-md-4{margin-bottom:24px !important}.ml-md-4{margin-left:24px !important}.mt-md-n4{margin-top:-24px !important}.mr-md-n4{margin-right:-24px !important}.mb-md-n4{margin-bottom:-24px !important}.ml-md-n4{margin-left:-24px !important}.mx-md-4{margin-right:24px !important;margin-left:24px !important}.my-md-4{margin-top:24px !important;margin-bottom:24px !important}.m-md-5{margin:32px !important}.mt-md-5{margin-top:32px !important}.mr-md-5{margin-right:32px !important}.mb-md-5{margin-bottom:32px !important}.ml-md-5{margin-left:32px !important}.mt-md-n5{margin-top:-32px !important}.mr-md-n5{margin-right:-32px !important}.mb-md-n5{margin-bottom:-32px !important}.ml-md-n5{margin-left:-32px !important}.mx-md-5{margin-right:32px !important;margin-left:32px !important}.my-md-5{margin-top:32px !important;margin-bottom:32px !important}.m-md-6{margin:40px !important}.mt-md-6{margin-top:40px !important}.mr-md-6{margin-right:40px !important}.mb-md-6{margin-bottom:40px !important}.ml-md-6{margin-left:40px !important}.mt-md-n6{margin-top:-40px !important}.mr-md-n6{margin-right:-40px !important}.mb-md-n6{margin-bottom:-40px !important}.ml-md-n6{margin-left:-40px !important}.mx-md-6{margin-right:40px !important;margin-left:40px !important}.my-md-6{margin-top:40px !important;margin-bottom:40px !important}.mx-md-auto{margin-right:auto !important;margin-left:auto !important}}@media (min-width: 1012px){.m-lg-0{margin:0 !important}.mt-lg-0{margin-top:0 !important}.mr-lg-0{margin-right:0 !important}.mb-lg-0{margin-bottom:0 !important}.ml-lg-0{margin-left:0 !important}.mx-lg-0{margin-right:0 !important;margin-left:0 !important}.my-lg-0{margin-top:0 !important;margin-bottom:0 !important}.m-lg-1{margin:4px !important}.mt-lg-1{margin-top:4px !important}.mr-lg-1{margin-right:4px !important}.mb-lg-1{margin-bottom:4px !important}.ml-lg-1{margin-left:4px !important}.mt-lg-n1{margin-top:-4px !important}.mr-lg-n1{margin-right:-4px !important}.mb-lg-n1{margin-bottom:-4px !important}.ml-lg-n1{margin-left:-4px !important}.mx-lg-1{margin-right:4px !important;margin-left:4px !important}.my-lg-1{margin-top:4px !important;margin-bottom:4px !important}.m-lg-2{margin:8px !important}.mt-lg-2{margin-top:8px !important}.mr-lg-2{margin-right:8px !important}.mb-lg-2{margin-bottom:8px !important}.ml-lg-2{margin-left:8px !important}.mt-lg-n2{margin-top:-8px !important}.mr-lg-n2{margin-right:-8px !important}.mb-lg-n2{margin-bottom:-8px !important}.ml-lg-n2{margin-left:-8px !important}.mx-lg-2{margin-right:8px !important;margin-left:8px !important}.my-lg-2{margin-top:8px !important;margin-bottom:8px !important}.m-lg-3{margin:16px !important}.mt-lg-3{margin-top:16px !important}.mr-lg-3{margin-right:16px !important}.mb-lg-3{margin-bottom:16px !important}.ml-lg-3{margin-left:16px !important}.mt-lg-n3{margin-top:-16px !important}.mr-lg-n3{margin-right:-16px !important}.mb-lg-n3{margin-bottom:-16px !important}.ml-lg-n3{margin-left:-16px !important}.mx-lg-3{margin-right:16px !important;margin-left:16px !important}.my-lg-3{margin-top:16px !important;margin-bottom:16px !important}.m-lg-4{margin:24px !important}.mt-lg-4{margin-top:24px !important}.mr-lg-4{margin-right:24px !important}.mb-lg-4{margin-bottom:24px !important}.ml-lg-4{margin-left:24px !important}.mt-lg-n4{margin-top:-24px !important}.mr-lg-n4{margin-right:-24px !important}.mb-lg-n4{margin-bottom:-24px !important}.ml-lg-n4{margin-left:-24px !important}.mx-lg-4{margin-right:24px !important;margin-left:24px !important}.my-lg-4{margin-top:24px !important;margin-bottom:24px !important}.m-lg-5{margin:32px !important}.mt-lg-5{margin-top:32px !important}.mr-lg-5{margin-right:32px !important}.mb-lg-5{margin-bottom:32px !important}.ml-lg-5{margin-left:32px !important}.mt-lg-n5{margin-top:-32px !important}.mr-lg-n5{margin-right:-32px !important}.mb-lg-n5{margin-bottom:-32px !important}.ml-lg-n5{margin-left:-32px !important}.mx-lg-5{margin-right:32px !important;margin-left:32px !important}.my-lg-5{margin-top:32px !important;margin-bottom:32px !important}.m-lg-6{margin:40px !important}.mt-lg-6{margin-top:40px !important}.mr-lg-6{margin-right:40px !important}.mb-lg-6{margin-bottom:40px !important}.ml-lg-6{margin-left:40px !important}.mt-lg-n6{margin-top:-40px !important}.mr-lg-n6{margin-right:-40px !important}.mb-lg-n6{margin-bottom:-40px !important}.ml-lg-n6{margin-left:-40px !important}.mx-lg-6{margin-right:40px !important;margin-left:40px !important}.my-lg-6{margin-top:40px !important;margin-bottom:40px !important}.mx-lg-auto{margin-right:auto !important;margin-left:auto !important}}@media (min-width: 1280px){.m-xl-0{margin:0 !important}.mt-xl-0{margin-top:0 !important}.mr-xl-0{margin-right:0 !important}.mb-xl-0{margin-bottom:0 !important}.ml-xl-0{margin-left:0 !important}.mx-xl-0{margin-right:0 !important;margin-left:0 !important}.my-xl-0{margin-top:0 !important;margin-bottom:0 !important}.m-xl-1{margin:4px !important}.mt-xl-1{margin-top:4px !important}.mr-xl-1{margin-right:4px !important}.mb-xl-1{margin-bottom:4px !important}.ml-xl-1{margin-left:4px !important}.mt-xl-n1{margin-top:-4px !important}.mr-xl-n1{margin-right:-4px !important}.mb-xl-n1{margin-bottom:-4px !important}.ml-xl-n1{margin-left:-4px !important}.mx-xl-1{margin-right:4px !important;margin-left:4px !important}.my-xl-1{margin-top:4px !important;margin-bottom:4px !important}.m-xl-2{margin:8px !important}.mt-xl-2{margin-top:8px !important}.mr-xl-2{margin-right:8px !important}.mb-xl-2{margin-bottom:8px !important}.ml-xl-2{margin-left:8px !important}.mt-xl-n2{margin-top:-8px !important}.mr-xl-n2{margin-right:-8px !important}.mb-xl-n2{margin-bottom:-8px !important}.ml-xl-n2{margin-left:-8px !important}.mx-xl-2{margin-right:8px !important;margin-left:8px !important}.my-xl-2{margin-top:8px !important;margin-bottom:8px !important}.m-xl-3{margin:16px !important}.mt-xl-3{margin-top:16px !important}.mr-xl-3{margin-right:16px !important}.mb-xl-3{margin-bottom:16px !important}.ml-xl-3{margin-left:16px !important}.mt-xl-n3{margin-top:-16px !important}.mr-xl-n3{margin-right:-16px !important}.mb-xl-n3{margin-bottom:-16px !important}.ml-xl-n3{margin-left:-16px !important}.mx-xl-3{margin-right:16px !important;margin-left:16px !important}.my-xl-3{margin-top:16px !important;margin-bottom:16px !important}.m-xl-4{margin:24px !important}.mt-xl-4{margin-top:24px !important}.mr-xl-4{margin-right:24px !important}.mb-xl-4{margin-bottom:24px !important}.ml-xl-4{margin-left:24px !important}.mt-xl-n4{margin-top:-24px !important}.mr-xl-n4{margin-right:-24px !important}.mb-xl-n4{margin-bottom:-24px !important}.ml-xl-n4{margin-left:-24px !important}.mx-xl-4{margin-right:24px !important;margin-left:24px !important}.my-xl-4{margin-top:24px !important;margin-bottom:24px !important}.m-xl-5{margin:32px !important}.mt-xl-5{margin-top:32px !important}.mr-xl-5{margin-right:32px !important}.mb-xl-5{margin-bottom:32px !important}.ml-xl-5{margin-left:32px !important}.mt-xl-n5{margin-top:-32px !important}.mr-xl-n5{margin-right:-32px !important}.mb-xl-n5{margin-bottom:-32px !important}.ml-xl-n5{margin-left:-32px !important}.mx-xl-5{margin-right:32px !important;margin-left:32px !important}.my-xl-5{margin-top:32px !important;margin-bottom:32px !important}.m-xl-6{margin:40px !important}.mt-xl-6{margin-top:40px !important}.mr-xl-6{margin-right:40px !important}.mb-xl-6{margin-bottom:40px !important}.ml-xl-6{margin-left:40px !important}.mt-xl-n6{margin-top:-40px !important}.mr-xl-n6{margin-right:-40px !important}.mb-xl-n6{margin-bottom:-40px !important}.ml-xl-n6{margin-left:-40px !important}.mx-xl-6{margin-right:40px !important;margin-left:40px !important}.my-xl-6{margin-top:40px !important;margin-bottom:40px !important}.mx-xl-auto{margin-right:auto !important;margin-left:auto !important}}.p-0{padding:0 !important}.pt-0{padding-top:0 !important}.pr-0{padding-right:0 !important}.pb-0{padding-bottom:0 !important}.pl-0{padding-left:0 !important}.px-0{padding-right:0 !important;padding-left:0 !important}.py-0{padding-top:0 !important;padding-bottom:0 !important}.p-1{padding:4px !important}.pt-1{padding-top:4px !important}.pr-1{padding-right:4px !important}.pb-1{padding-bottom:4px !important}.pl-1{padding-left:4px !important}.px-1{padding-right:4px !important;padding-left:4px !important}.py-1{padding-top:4px !important;padding-bottom:4px !important}.p-2{padding:8px !important}.pt-2{padding-top:8px !important}.pr-2{padding-right:8px !important}.pb-2{padding-bottom:8px !important}.pl-2{padding-left:8px !important}.px-2{padding-right:8px !important;padding-left:8px !important}.py-2{padding-top:8px !important;padding-bottom:8px !important}.p-3{padding:16px !important}.pt-3{padding-top:16px !important}.pr-3{padding-right:16px !important}.pb-3{padding-bottom:16px !important}.pl-3{padding-left:16px !important}.px-3{padding-right:16px !important;padding-left:16px !important}.py-3{padding-top:16px !important;padding-bottom:16px !important}.p-4{padding:24px !important}.pt-4{padding-top:24px !important}.pr-4{padding-right:24px !important}.pb-4{padding-bottom:24px !important}.pl-4{padding-left:24px !important}.px-4{padding-right:24px !important;padding-left:24px !important}.py-4{padding-top:24px !important;padding-bottom:24px !important}.p-5{padding:32px !important}.pt-5{padding-top:32px !important}.pr-5{padding-right:32px !important}.pb-5{padding-bottom:32px !important}.pl-5{padding-left:32px !important}.px-5{padding-right:32px !important;padding-left:32px !important}.py-5{padding-top:32px !important;padding-bottom:32px !important}.p-6{padding:40px !important}.pt-6{padding-top:40px !important}.pr-6{padding-right:40px !important}.pb-6{padding-bottom:40px !important}.pl-6{padding-left:40px !important}.px-6{padding-right:40px !important;padding-left:40px !important}.py-6{padding-top:40px !important;padding-bottom:40px !important}@media (min-width: 544px){.p-sm-0{padding:0 !important}.pt-sm-0{padding-top:0 !important}.pr-sm-0{padding-right:0 !important}.pb-sm-0{padding-bottom:0 !important}.pl-sm-0{padding-left:0 !important}.px-sm-0{padding-right:0 !important;padding-left:0 !important}.py-sm-0{padding-top:0 !important;padding-bottom:0 !important}.p-sm-1{padding:4px !important}.pt-sm-1{padding-top:4px !important}.pr-sm-1{padding-right:4px !important}.pb-sm-1{padding-bottom:4px !important}.pl-sm-1{padding-left:4px !important}.px-sm-1{padding-right:4px !important;padding-left:4px !important}.py-sm-1{padding-top:4px !important;padding-bottom:4px !important}.p-sm-2{padding:8px !important}.pt-sm-2{padding-top:8px !important}.pr-sm-2{padding-right:8px !important}.pb-sm-2{padding-bottom:8px !important}.pl-sm-2{padding-left:8px !important}.px-sm-2{padding-right:8px !important;padding-left:8px !important}.py-sm-2{padding-top:8px !important;padding-bottom:8px !important}.p-sm-3{padding:16px !important}.pt-sm-3{padding-top:16px !important}.pr-sm-3{padding-right:16px !important}.pb-sm-3{padding-bottom:16px !important}.pl-sm-3{padding-left:16px !important}.px-sm-3{padding-right:16px !important;padding-left:16px !important}.py-sm-3{padding-top:16px !important;padding-bottom:16px !important}.p-sm-4{padding:24px !important}.pt-sm-4{padding-top:24px !important}.pr-sm-4{padding-right:24px !important}.pb-sm-4{padding-bottom:24px !important}.pl-sm-4{padding-left:24px !important}.px-sm-4{padding-right:24px !important;padding-left:24px !important}.py-sm-4{padding-top:24px !important;padding-bottom:24px !important}.p-sm-5{padding:32px !important}.pt-sm-5{padding-top:32px !important}.pr-sm-5{padding-right:32px !important}.pb-sm-5{padding-bottom:32px !important}.pl-sm-5{padding-left:32px !important}.px-sm-5{padding-right:32px !important;padding-left:32px !important}.py-sm-5{padding-top:32px !important;padding-bottom:32px !important}.p-sm-6{padding:40px !important}.pt-sm-6{padding-top:40px !important}.pr-sm-6{padding-right:40px !important}.pb-sm-6{padding-bottom:40px !important}.pl-sm-6{padding-left:40px !important}.px-sm-6{padding-right:40px !important;padding-left:40px !important}.py-sm-6{padding-top:40px !important;padding-bottom:40px !important}}@media (min-width: 768px){.p-md-0{padding:0 !important}.pt-md-0{padding-top:0 !important}.pr-md-0{padding-right:0 !important}.pb-md-0{padding-bottom:0 !important}.pl-md-0{padding-left:0 !important}.px-md-0{padding-right:0 !important;padding-left:0 !important}.py-md-0{padding-top:0 !important;padding-bottom:0 !important}.p-md-1{padding:4px !important}.pt-md-1{padding-top:4px !important}.pr-md-1{padding-right:4px !important}.pb-md-1{padding-bottom:4px !important}.pl-md-1{padding-left:4px !important}.px-md-1{padding-right:4px !important;padding-left:4px !important}.py-md-1{padding-top:4px !important;padding-bottom:4px !important}.p-md-2{padding:8px !important}.pt-md-2{padding-top:8px !important}.pr-md-2{padding-right:8px !important}.pb-md-2{padding-bottom:8px !important}.pl-md-2{padding-left:8px !important}.px-md-2{padding-right:8px !important;padding-left:8px !important}.py-md-2{padding-top:8px !important;padding-bottom:8px !important}.p-md-3{padding:16px !important}.pt-md-3{padding-top:16px !important}.pr-md-3{padding-right:16px !important}.pb-md-3{padding-bottom:16px !important}.pl-md-3{padding-left:16px !important}.px-md-3{padding-right:16px !important;padding-left:16px !important}.py-md-3{padding-top:16px !important;padding-bottom:16px !important}.p-md-4{padding:24px !important}.pt-md-4{padding-top:24px !important}.pr-md-4{padding-right:24px !important}.pb-md-4{padding-bottom:24px !important}.pl-md-4{padding-left:24px !important}.px-md-4{padding-right:24px !important;padding-left:24px !important}.py-md-4{padding-top:24px !important;padding-bottom:24px !important}.p-md-5{padding:32px !important}.pt-md-5{padding-top:32px !important}.pr-md-5{padding-right:32px !important}.pb-md-5{padding-bottom:32px !important}.pl-md-5{padding-left:32px !important}.px-md-5{padding-right:32px !important;padding-left:32px !important}.py-md-5{padding-top:32px !important;padding-bottom:32px !important}.p-md-6{padding:40px !important}.pt-md-6{padding-top:40px !important}.pr-md-6{padding-right:40px !important}.pb-md-6{padding-bottom:40px !important}.pl-md-6{padding-left:40px !important}.px-md-6{padding-right:40px !important;padding-left:40px !important}.py-md-6{padding-top:40px !important;padding-bottom:40px !important}}@media (min-width: 1012px){.p-lg-0{padding:0 !important}.pt-lg-0{padding-top:0 !important}.pr-lg-0{padding-right:0 !important}.pb-lg-0{padding-bottom:0 !important}.pl-lg-0{padding-left:0 !important}.px-lg-0{padding-right:0 !important;padding-left:0 !important}.py-lg-0{padding-top:0 !important;padding-bottom:0 !important}.p-lg-1{padding:4px !important}.pt-lg-1{padding-top:4px !important}.pr-lg-1{padding-right:4px !important}.pb-lg-1{padding-bottom:4px !important}.pl-lg-1{padding-left:4px !important}.px-lg-1{padding-right:4px !important;padding-left:4px !important}.py-lg-1{padding-top:4px !important;padding-bottom:4px !important}.p-lg-2{padding:8px !important}.pt-lg-2{padding-top:8px !important}.pr-lg-2{padding-right:8px !important}.pb-lg-2{padding-bottom:8px !important}.pl-lg-2{padding-left:8px !important}.px-lg-2{padding-right:8px !important;padding-left:8px !important}.py-lg-2{padding-top:8px !important;padding-bottom:8px !important}.p-lg-3{padding:16px !important}.pt-lg-3{padding-top:16px !important}.pr-lg-3{padding-right:16px !important}.pb-lg-3{padding-bottom:16px !important}.pl-lg-3{padding-left:16px !important}.px-lg-3{padding-right:16px !important;padding-left:16px !important}.py-lg-3{padding-top:16px !important;padding-bottom:16px !important}.p-lg-4{padding:24px !important}.pt-lg-4{padding-top:24px !important}.pr-lg-4{padding-right:24px !important}.pb-lg-4{padding-bottom:24px !important}.pl-lg-4{padding-left:24px !important}.px-lg-4{padding-right:24px !important;padding-left:24px !important}.py-lg-4{padding-top:24px !important;padding-bottom:24px !important}.p-lg-5{padding:32px !important}.pt-lg-5{padding-top:32px !important}.pr-lg-5{padding-right:32px !important}.pb-lg-5{padding-bottom:32px !important}.pl-lg-5{padding-left:32px !important}.px-lg-5{padding-right:32px !important;padding-left:32px !important}.py-lg-5{padding-top:32px !important;padding-bottom:32px !important}.p-lg-6{padding:40px !important}.pt-lg-6{padding-top:40px !important}.pr-lg-6{padding-right:40px !important}.pb-lg-6{padding-bottom:40px !important}.pl-lg-6{padding-left:40px !important}.px-lg-6{padding-right:40px !important;padding-left:40px !important}.py-lg-6{padding-top:40px !important;padding-bottom:40px !important}}@media (min-width: 1280px){.p-xl-0{padding:0 !important}.pt-xl-0{padding-top:0 !important}.pr-xl-0{padding-right:0 !important}.pb-xl-0{padding-bottom:0 !important}.pl-xl-0{padding-left:0 !important}.px-xl-0{padding-right:0 !important;padding-left:0 !important}.py-xl-0{padding-top:0 !important;padding-bottom:0 !important}.p-xl-1{padding:4px !important}.pt-xl-1{padding-top:4px !important}.pr-xl-1{padding-right:4px !important}.pb-xl-1{padding-bottom:4px !important}.pl-xl-1{padding-left:4px !important}.px-xl-1{padding-right:4px !important;padding-left:4px !important}.py-xl-1{padding-top:4px !important;padding-bottom:4px !important}.p-xl-2{padding:8px !important}.pt-xl-2{padding-top:8px !important}.pr-xl-2{padding-right:8px !important}.pb-xl-2{padding-bottom:8px !important}.pl-xl-2{padding-left:8px !important}.px-xl-2{padding-right:8px !important;padding-left:8px !important}.py-xl-2{padding-top:8px !important;padding-bottom:8px !important}.p-xl-3{padding:16px !important}.pt-xl-3{padding-top:16px !important}.pr-xl-3{padding-right:16px !important}.pb-xl-3{padding-bottom:16px !important}.pl-xl-3{padding-left:16px !important}.px-xl-3{padding-right:16px !important;padding-left:16px !important}.py-xl-3{padding-top:16px !important;padding-bottom:16px !important}.p-xl-4{padding:24px !important}.pt-xl-4{padding-top:24px !important}.pr-xl-4{padding-right:24px !important}.pb-xl-4{padding-bottom:24px !important}.pl-xl-4{padding-left:24px !important}.px-xl-4{padding-right:24px !important;padding-left:24px !important}.py-xl-4{padding-top:24px !important;padding-bottom:24px !important}.p-xl-5{padding:32px !important}.pt-xl-5{padding-top:32px !important}.pr-xl-5{padding-right:32px !important}.pb-xl-5{padding-bottom:32px !important}.pl-xl-5{padding-left:32px !important}.px-xl-5{padding-right:32px !important;padding-left:32px !important}.py-xl-5{padding-top:32px !important;padding-bottom:32px !important}.p-xl-6{padding:40px !important}.pt-xl-6{padding-top:40px !important}.pr-xl-6{padding-right:40px !important}.pb-xl-6{padding-bottom:40px !important}.pl-xl-6{padding-left:40px !important}.px-xl-6{padding-right:40px !important;padding-left:40px !important}.py-xl-6{padding-top:40px !important;padding-bottom:40px !important}}.p-responsive{padding-right:16px !important;padding-left:16px !important}@media (min-width: 544px){.p-responsive{padding-right:40px !important;padding-left:40px !important}}@media (min-width: 1012px){.p-responsive{padding-right:16px !important;padding-left:16px !important}}.h1{font-size:26px !important}@media (min-width: 768px){.h1{font-size:32px !important}}.h2{font-size:22px !important}@media (min-width: 768px){.h2{font-size:24px !important}}.h3{font-size:18px !important}@media (min-width: 768px){.h3{font-size:20px !important}}.h4{font-size:16px !important}.h5{font-size:14px !important}.h6{font-size:12px !important}.h1,.h2,.h3,.h4,.h5,.h6{font-weight:600 !important}.f1{font-size:26px !important}@media (min-width: 768px){.f1{font-size:32px !important}}.f2{font-size:22px !important}@media (min-width: 768px){.f2{font-size:24px !important}}.f3{font-size:18px !important}@media (min-width: 768px){.f3{font-size:20px !important}}.f4{font-size:16px !important}@media (min-width: 768px){.f4{font-size:16px !important}}.f5{font-size:14px !important}.f6{font-size:12px !important}.f00-light{font-size:40px !important;font-weight:300 !important}@media (min-width: 768px){.f00-light{font-size:48px !important}}.f0-light{font-size:32px !important;font-weight:300 !important}@media (min-width: 768px){.f0-light{font-size:40px !important}}.f1-light{font-size:26px !important;font-weight:300 !important}@media (min-width: 768px){.f1-light{font-size:32px !important}}.f2-light{font-size:22px !important;font-weight:300 !important}@media (min-width: 768px){.f2-light{font-size:24px !important}}.f3-light{font-size:18px !important;font-weight:300 !important}@media (min-width: 768px){.f3-light{font-size:20px !important}}.text-small{font-size:12px !important}.lead{margin-bottom:30px;font-size:20px;font-weight:300;color:#586069}.lh-condensed-ultra{line-height:1 !important}.lh-condensed{line-height:1.25 !important}.lh-default{line-height:1.5 !important}.lh-0{line-height:0 !important}.text-right{text-align:right !important}.text-left{text-align:left !important}.text-center{text-align:center !important}@media (min-width: 544px){.text-sm-right{text-align:right !important}.text-sm-left{text-align:left !important}.text-sm-center{text-align:center !important}}@media (min-width: 768px){.text-md-right{text-align:right !important}.text-md-left{text-align:left !important}.text-md-center{text-align:center !important}}@media (min-width: 1012px){.text-lg-right{text-align:right !important}.text-lg-left{text-align:left !important}.text-lg-center{text-align:center !important}}@media (min-width: 1280px){.text-xl-right{text-align:right !important}.text-xl-left{text-align:left !important}.text-xl-center{text-align:center !important}}.text-normal{font-weight:400 !important}.text-bold{font-weight:600 !important}.text-italic{font-style:italic !important}.text-uppercase{text-transform:uppercase !important}.text-underline{text-decoration:underline !important}.no-underline{text-decoration:none !important}.no-wrap{white-space:nowrap !important}.ws-normal{white-space:normal !important}.wb-break-all{word-break:break-all !important}.text-emphasized{font-weight:600;color:#24292e}.list-style-none{list-style:none !important}.text-shadow-dark{text-shadow:0 1px 1px rgba(27,31,35,0.25),0 1px 25px rgba(27,31,35,0.75)}.text-shadow-light{text-shadow:0 1px 0 rgba(255,255,255,0.5)}.text-mono{font-family:\"SFMono-Regular\",Consolas,\"Liberation Mono\",Menlo,Courier,monospace}.user-select-none{user-select:none !important}.d-block{display:block !important}.d-flex{display:flex !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-inline-flex{display:inline-flex !important}.d-none{display:none !important}.d-table{display:table !important}.d-table-cell{display:table-cell !important}@media (min-width: 544px){.d-sm-block{display:block !important}.d-sm-flex{display:flex !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-inline-flex{display:inline-flex !important}.d-sm-none{display:none !important}.d-sm-table{display:table !important}.d-sm-table-cell{display:table-cell !important}}@media (min-width: 768px){.d-md-block{display:block !important}.d-md-flex{display:flex !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-inline-flex{display:inline-flex !important}.d-md-none{display:none !important}.d-md-table{display:table !important}.d-md-table-cell{display:table-cell !important}}@media (min-width: 1012px){.d-lg-block{display:block !important}.d-lg-flex{display:flex !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-inline-flex{display:inline-flex !important}.d-lg-none{display:none !important}.d-lg-table{display:table !important}.d-lg-table-cell{display:table-cell !important}}@media (min-width: 1280px){.d-xl-block{display:block !important}.d-xl-flex{display:flex !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-inline-flex{display:inline-flex !important}.d-xl-none{display:none !important}.d-xl-table{display:table !important}.d-xl-table-cell{display:table-cell !important}}.v-hidden{visibility:hidden !important}.v-visible{visibility:visible !important}@media (max-width: 544px){.hide-sm{display:none !important}}@media (min-width: 544px) and (max-width: 768px){.hide-md{display:none !important}}@media (min-width: 768px) and (max-width: 1012px){.hide-lg{display:none !important}}@media (min-width: 1012px){.hide-xl{display:none !important}}.table-fixed{table-layout:fixed !important}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);word-wrap:normal;border:0}.show-on-focus{position:absolute;width:1px;height:1px;margin:0;overflow:hidden;clip:rect(1px, 1px, 1px, 1px)}.show-on-focus:focus{z-index:20;width:auto;height:auto;clip:auto}.container{width:980px;margin-right:auto;margin-left:auto}.container::before{display:table;content:\"\"}.container::after{display:table;clear:both;content:\"\"}.container-md{max-width:768px;margin-right:auto;margin-left:auto}.container-lg{max-width:1012px;margin-right:auto;margin-left:auto}.container-xl{max-width:1280px;margin-right:auto;margin-left:auto}.columns{margin-right:-10px;margin-left:-10px}.columns::before{display:table;content:\"\"}.columns::after{display:table;clear:both;content:\"\"}.column{float:left;padding-right:10px;padding-left:10px}.one-third{width:33.333333%}.two-thirds{width:66.666667%}.one-fourth{width:25%}.one-half{width:50%}.three-fourths{width:75%}.one-fifth{width:20%}.four-fifths{width:80%}.centered{display:block;float:none;margin-right:auto;margin-left:auto}.col-1{width:8.3333333333%}.col-2{width:16.6666666667%}.col-3{width:25%}.col-4{width:33.3333333333%}.col-5{width:41.6666666667%}.col-6{width:50%}.col-7{width:58.3333333333%}.col-8{width:66.6666666667%}.col-9{width:75%}.col-10{width:83.3333333333%}.col-11{width:91.6666666667%}.col-12{width:100%}@media (min-width: 544px){.col-sm-1{width:8.3333333333%}.col-sm-2{width:16.6666666667%}.col-sm-3{width:25%}.col-sm-4{width:33.3333333333%}.col-sm-5{width:41.6666666667%}.col-sm-6{width:50%}.col-sm-7{width:58.3333333333%}.col-sm-8{width:66.6666666667%}.col-sm-9{width:75%}.col-sm-10{width:83.3333333333%}.col-sm-11{width:91.6666666667%}.col-sm-12{width:100%}}@media (min-width: 768px){.col-md-1{width:8.3333333333%}.col-md-2{width:16.6666666667%}.col-md-3{width:25%}.col-md-4{width:33.3333333333%}.col-md-5{width:41.6666666667%}.col-md-6{width:50%}.col-md-7{width:58.3333333333%}.col-md-8{width:66.6666666667%}.col-md-9{width:75%}.col-md-10{width:83.3333333333%}.col-md-11{width:91.6666666667%}.col-md-12{width:100%}}@media (min-width: 1012px){.col-lg-1{width:8.3333333333%}.col-lg-2{width:16.6666666667%}.col-lg-3{width:25%}.col-lg-4{width:33.3333333333%}.col-lg-5{width:41.6666666667%}.col-lg-6{width:50%}.col-lg-7{width:58.3333333333%}.col-lg-8{width:66.6666666667%}.col-lg-9{width:75%}.col-lg-10{width:83.3333333333%}.col-lg-11{width:91.6666666667%}.col-lg-12{width:100%}}@media (min-width: 1280px){.col-xl-1{width:8.3333333333%}.col-xl-2{width:16.6666666667%}.col-xl-3{width:25%}.col-xl-4{width:33.3333333333%}.col-xl-5{width:41.6666666667%}.col-xl-6{width:50%}.col-xl-7{width:58.3333333333%}.col-xl-8{width:66.6666666667%}.col-xl-9{width:75%}.col-xl-10{width:83.3333333333%}.col-xl-11{width:91.6666666667%}.col-xl-12{width:100%}}.gutter{margin-right:-16px;margin-left:-16px}.gutter>[class*=\"col-\"]{padding-right:16px !important;padding-left:16px !important}.gutter-condensed{margin-right:-8px;margin-left:-8px}.gutter-condensed>[class*=\"col-\"]{padding-right:8px !important;padding-left:8px !important}.gutter-spacious{margin-right:-24px;margin-left:-24px}.gutter-spacious>[class*=\"col-\"]{padding-right:24px !important;padding-left:24px !important}@media (min-width: 544px){.gutter-sm{margin-right:-16px;margin-left:-16px}.gutter-sm>[class*=\"col-\"]{padding-right:16px !important;padding-left:16px !important}.gutter-sm-condensed{margin-right:-8px;margin-left:-8px}.gutter-sm-condensed>[class*=\"col-\"]{padding-right:8px !important;padding-left:8px !important}.gutter-sm-spacious{margin-right:-24px;margin-left:-24px}.gutter-sm-spacious>[class*=\"col-\"]{padding-right:24px !important;padding-left:24px !important}}@media (min-width: 768px){.gutter-md{margin-right:-16px;margin-left:-16px}.gutter-md>[class*=\"col-\"]{padding-right:16px !important;padding-left:16px !important}.gutter-md-condensed{margin-right:-8px;margin-left:-8px}.gutter-md-condensed>[class*=\"col-\"]{padding-right:8px !important;padding-left:8px !important}.gutter-md-spacious{margin-right:-24px;margin-left:-24px}.gutter-md-spacious>[class*=\"col-\"]{padding-right:24px !important;padding-left:24px !important}}@media (min-width: 1012px){.gutter-lg{margin-right:-16px;margin-left:-16px}.gutter-lg>[class*=\"col-\"]{padding-right:16px !important;padding-left:16px !important}.gutter-lg-condensed{margin-right:-8px;margin-left:-8px}.gutter-lg-condensed>[class*=\"col-\"]{padding-right:8px !important;padding-left:8px !important}.gutter-lg-spacious{margin-right:-24px;margin-left:-24px}.gutter-lg-spacious>[class*=\"col-\"]{padding-right:24px !important;padding-left:24px !important}}@media (min-width: 1280px){.gutter-xl{margin-right:-16px;margin-left:-16px}.gutter-xl>[class*=\"col-\"]{padding-right:16px !important;padding-left:16px !important}.gutter-xl-condensed{margin-right:-8px;margin-left:-8px}.gutter-xl-condensed>[class*=\"col-\"]{padding-right:8px !important;padding-left:8px !important}.gutter-xl-spacious{margin-right:-24px;margin-left:-24px}.gutter-xl-spacious>[class*=\"col-\"]{padding-right:24px !important;padding-left:24px !important}}.offset-1{margin-left:8.3333333333% !important}.offset-2{margin-left:16.6666666667% !important}.offset-3{margin-left:25% !important}.offset-4{margin-left:33.3333333333% !important}.offset-5{margin-left:41.6666666667% !important}.offset-6{margin-left:50% !important}.offset-7{margin-left:58.3333333333% !important}.offset-8{margin-left:66.6666666667% !important}.offset-9{margin-left:75% !important}.offset-10{margin-left:83.3333333333% !important}.offset-11{margin-left:91.6666666667% !important}@media (min-width: 544px){.offset-sm-1{margin-left:8.3333333333% !important}.offset-sm-2{margin-left:16.6666666667% !important}.offset-sm-3{margin-left:25% !important}.offset-sm-4{margin-left:33.3333333333% !important}.offset-sm-5{margin-left:41.6666666667% !important}.offset-sm-6{margin-left:50% !important}.offset-sm-7{margin-left:58.3333333333% !important}.offset-sm-8{margin-left:66.6666666667% !important}.offset-sm-9{margin-left:75% !important}.offset-sm-10{margin-left:83.3333333333% !important}.offset-sm-11{margin-left:91.6666666667% !important}}@media (min-width: 768px){.offset-md-1{margin-left:8.3333333333% !important}.offset-md-2{margin-left:16.6666666667% !important}.offset-md-3{margin-left:25% !important}.offset-md-4{margin-left:33.3333333333% !important}.offset-md-5{margin-left:41.6666666667% !important}.offset-md-6{margin-left:50% !important}.offset-md-7{margin-left:58.3333333333% !important}.offset-md-8{margin-left:66.6666666667% !important}.offset-md-9{margin-left:75% !important}.offset-md-10{margin-left:83.3333333333% !important}.offset-md-11{margin-left:91.6666666667% !important}}@media (min-width: 1012px){.offset-lg-1{margin-left:8.3333333333% !important}.offset-lg-2{margin-left:16.6666666667% !important}.offset-lg-3{margin-left:25% !important}.offset-lg-4{margin-left:33.3333333333% !important}.offset-lg-5{margin-left:41.6666666667% !important}.offset-lg-6{margin-left:50% !important}.offset-lg-7{margin-left:58.3333333333% !important}.offset-lg-8{margin-left:66.6666666667% !important}.offset-lg-9{margin-left:75% !important}.offset-lg-10{margin-left:83.3333333333% !important}.offset-lg-11{margin-left:91.6666666667% !important}}@media (min-width: 1280px){.offset-xl-1{margin-left:8.3333333333% !important}.offset-xl-2{margin-left:16.6666666667% !important}.offset-xl-3{margin-left:25% !important}.offset-xl-4{margin-left:33.3333333333% !important}.offset-xl-5{margin-left:41.6666666667% !important}.offset-xl-6{margin-left:50% !important}.offset-xl-7{margin-left:58.3333333333% !important}.offset-xl-8{margin-left:66.6666666667% !important}.offset-xl-9{margin-left:75% !important}.offset-xl-10{margin-left:83.3333333333% !important}.offset-xl-11{margin-left:91.6666666667% !important}}.markdown-body{font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\";font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body::before{display:table;content:\"\"}.markdown-body::after{display:table;clear:both;content:\"\"}.markdown-body>*:first-child{margin-top:0 !important}.markdown-body>*:last-child{margin-bottom:0 !important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:#cb2431}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px;line-height:1}.markdown-body .anchor:focus{outline:none}.markdown-body p,.markdown-body blockquote,.markdown-body ul,.markdown-body ol,.markdown-body dl,.markdown-body table,.markdown-body pre{margin-top:0;margin-bottom:16px}.markdown-body hr{height:.25em;padding:0;margin:24px 0;background-color:#e1e4e8;border:0}.markdown-body blockquote{padding:0 1em;color:#6a737d;border-left:0.25em solid #dfe2e5}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body kbd{display:inline-block;padding:3px 5px;font-size:11px;line-height:10px;color:#444d56;vertical-align:middle;background-color:#fafbfc;border:solid 1px #c6cbd1;border-bottom-color:#959da5;border-radius:3px;box-shadow:inset 0 -1px 0 #959da5}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px;margin-bottom:16px;font-weight:600;line-height:1.25}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#1b1f23;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 tt,.markdown-body h1 code,.markdown-body h2 tt,.markdown-body h2 code,.markdown-body h3 tt,.markdown-body h3 code,.markdown-body h4 tt,.markdown-body h4 code,.markdown-body h5 tt,.markdown-body h5 code,.markdown-body h6 tt,.markdown-body h6 code{font-size:inherit}.markdown-body h1{padding-bottom:0.3em;font-size:2em;border-bottom:1px solid #eaecef}.markdown-body h2{padding-bottom:0.3em;font-size:1.5em;border-bottom:1px solid #eaecef}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:0.875em}.markdown-body h6{font-size:0.85em;color:#6a737d}.markdown-body ul,.markdown-body ol{padding-left:2em}.markdown-body ul.no-list,.markdown-body ol.no-list{padding:0;list-style-type:none}.markdown-body ul ul,.markdown-body ul ol,.markdown-body ol ol,.markdown-body ol ul{margin-top:0;margin-bottom:0}.markdown-body li{word-wrap:break-all}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic;font-weight:600}.markdown-body dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table{display:block;width:100%;overflow:auto}.markdown-body table th{font-weight:600}.markdown-body table th,.markdown-body table td{padding:6px 13px;border:1px solid #dfe2e5}.markdown-body table tr{background-color:#fff;border-top:1px solid #c6cbd1}.markdown-body table tr:nth-child(2n){background-color:#f6f8fa}.markdown-body table img{background-color:transparent}.markdown-body img{max-width:100%;box-sizing:content-box;background-color:#fff}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{max-width:none;vertical-align:text-top;background-color:transparent}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{display:block;float:left;width:auto;padding:7px;margin:13px 0 0;overflow:hidden;border:1px solid #dfe2e5}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{display:block;padding:5px 0 0;clear:both;color:#24292e}.markdown-body span.align-center{display:block;overflow:hidden;clear:both}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{display:block;overflow:hidden;clear:both}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{padding:0.2em 0.4em;margin:0;font-size:85%;background-color:rgba(27,31,35,0.05);border-radius:3px}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body pre{word-wrap:normal}.markdown-body pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:transparent;border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45;background-color:#f6f8fa;border-radius:3px}.markdown-body pre code,.markdown-body pre tt{display:inline;max-width:auto;padding:0;margin:0;overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body .csv-data td,.markdown-body .csv-data th{padding:5px;overflow:hidden;font-size:12px;line-height:1;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-num{padding:10px 8px 9px;text-align:right;background:#fff;border:0}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{font-weight:600;background:#f6f8fa;border-top:0}.highlight table td{padding:5px}.highlight table pre{margin:0}.highlight .cm{color:#999988;font-style:italic}.highlight .cp{color:#999999;font-weight:bold}.highlight .c1{color:#999988;font-style:italic}.highlight .cs{color:#999999;font-weight:bold;font-style:italic}.highlight .c,.highlight .cd{color:#999988;font-style:italic}.highlight .err{color:#a61717;background-color:#e3d2d2}.highlight .gd{color:#000000;background-color:#ffdddd}.highlight .ge{color:#000000;font-style:italic}.highlight .gr{color:#aa0000}.highlight .gh{color:#999999}.highlight .gi{color:#000000;background-color:#ddffdd}.highlight .go{color:#888888}.highlight .gp{color:#555555}.highlight .gs{font-weight:bold}.highlight .gu{color:#aaaaaa}.highlight .gt{color:#aa0000}.highlight .kc{color:#000000;font-weight:bold}.highlight .kd{color:#000000;font-weight:bold}.highlight .kn{color:#000000;font-weight:bold}.highlight .kp{color:#000000;font-weight:bold}.highlight .kr{color:#000000;font-weight:bold}.highlight .kt{color:#445588;font-weight:bold}.highlight .k,.highlight .kv{color:#000000;font-weight:bold}.highlight .mf{color:#009999}.highlight .mh{color:#009999}.highlight .il{color:#009999}.highlight .mi{color:#009999}.highlight .mo{color:#009999}.highlight .m,.highlight .mb,.highlight .mx{color:#009999}.highlight .sb{color:#d14}.highlight .sc{color:#d14}.highlight .sd{color:#d14}.highlight .s2{color:#d14}.highlight .se{color:#d14}.highlight .sh{color:#d14}.highlight .si{color:#d14}.highlight .sx{color:#d14}.highlight .sr{color:#009926}.highlight .s1{color:#d14}.highlight .ss{color:#990073}.highlight .s{color:#d14}.highlight .na{color:#008080}.highlight .bp{color:#999999}.highlight .nb{color:#0086B3}.highlight .nc{color:#445588;font-weight:bold}.highlight .no{color:#008080}.highlight .nd{color:#3c5d5d;font-weight:bold}.highlight .ni{color:#800080}.highlight .ne{color:#990000;font-weight:bold}.highlight .nf{color:#990000;font-weight:bold}.highlight .nl{color:#990000;font-weight:bold}.highlight .nn{color:#555555}.highlight .nt{color:#000080}.highlight .vc{color:#008080}.highlight .vg{color:#008080}.highlight .vi{color:#008080}.highlight .nv{color:#008080}.highlight .ow{color:#000000;font-weight:bold}.highlight .o{color:#000000;font-weight:bold}.highlight .w{color:#bbbbbb}.highlight{background-color:#f8f8f8}\n","<?xml version=\"1.0\" encoding=\"utf-8\"?><feed xmlns=\"http://www.w3.org/2005/Atom\" ><generator uri=\"https://jekyllrb.com/\" version=\"3.9.2\">Jekyll</generator><link href=\"https://www.mikecaptain.com/pages/Poechant/feed.xml\" rel=\"self\" type=\"application/atom+xml\" /><link href=\"https://www.mikecaptain.com/pages/Poechant/\" rel=\"alternate\" type=\"text/html\" /><updated>2023-01-03T19:14:34+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/feed.xml</id><title type=\"html\">麦克船长的技术、产品与商业博客</title><subtitle>麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管</subtitle><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><entry><title type=\"html\">自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2022/12/24/captain-nlp-1/\" rel=\"alternate\" type=\"text/html\" title=\"自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用\" /><published>2022-12-24T15:08:01+00:00</published><updated>2022-12-24T15:08:01+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2022/12/24/captain-nlp-1</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2022/12/24/captain-nlp-1/\"><ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><strong>本文目录</strong></p>\n<ul id="markdown-toc">\n <li><a href="#一自然语言处理领域近年的发展关键节点" id="markdown-toc-一自然语言处理领域近年的发展关键节点">一、自然语言处理领域近年的发展关键节点</a> <ul>\n <li><a href="#1从理性主义到经验主义" id="markdown-toc-1从理性主义到经验主义">1、从理性主义到经验主义</a></li>\n <li><a href="#2经验主义的早期还不是深度学习" id="markdown-toc-2经验主义的早期还不是深度学习">2、经验主义的早期,还不是深度学习</a></li>\n <li><a href="#3撇开特征让机器囫囵吞枣地学吧" id="markdown-toc-3撇开特征让机器囫囵吞枣地学吧">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\n <li><a href="#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工" id="markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\n <li><a href="#5自监督学习法让我们省去人工标注" id="markdown-toc-5自监督学习法让我们省去人工标注">5、自监督学习法,让我们省去人工标注</a></li>\n <li><a href="#6用原始的任务训练出来的模型能迁移去解决新任务吗" id="markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\n <li><a href="#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域" id="markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\n <li><a href="#8数据和算力有了还不够" id="markdown-toc-8数据和算力有了还不够">8、数据和算力有了,还不够</a></li>\n </ul>\n </li>\n <li><a href="#二学术里程碑几篇重量级论文" id="markdown-toc-二学术里程碑几篇重量级论文">二、学术里程碑:几篇重量级论文</a> <ul>\n <li><a href="#1提出-transformer-的attention-is-all-you-need2017" id="markdown-toc-1提出-transformer-的attention-is-all-you-need2017">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\n <li><a href="#2elmo-deep-contextualized-word-representations" id="markdown-toc-2elmo-deep-contextualized-word-representations">2、ELMo: Deep contextualized word representations</a></li>\n <li><a href="#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018" id="markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\n <li><a href="#4gpt-3-language-models-are-few-shot-learners2020" id="markdown-toc-4gpt-3-language-models-are-few-shot-learners2020">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\n <li><a href="#其他的重量级论文" id="markdown-toc-其他的重量级论文">其他的重量级论文</a></li>\n </ul>\n </li>\n <li><a href="#三行业里程碑" id="markdown-toc-三行业里程碑">三、行业里程碑</a></li>\n <li><a href="#四成本" id="markdown-toc-四成本">四、成本</a></li>\n <li><a href="#五业内应用" id="markdown-toc-五业内应用">五、业内应用</a></li>\n <li><a href="#五行业内哪些人的言论值得我们日常重点关注" id="markdown-toc-五行业内哪些人的言论值得我们日常重点关注">五、行业内哪些人的言论值得我们日常重点关注</a></li>\n <li><a href="#reference" id="markdown-toc-reference">Reference</a></li>\n</ul>\n\n<h3 id="一自然语言处理领域近年的发展关键节点">一、自然语言处理领域近年的发展关键节点</h3>\n\n<p><img src="/img/src/2022-12-17-ai-bert-1-1.jpg" alt="image" /></p>\n\n<h4 id="1从理性主义到经验主义">1、从理性主义到经验主义</h4>\n\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\n\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\n\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\n\n<h4 id="2经验主义的早期还不是深度学习">2、经验主义的早期,还不是深度学习</h4>\n\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\n\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\n\n<h4 id="3撇开特征让机器囫囵吞枣地学吧">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\n\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\n\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\n\n<h4 id="4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\n\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\n\n<h4 id="5自监督学习法让我们省去人工标注">5、自监督学习法,让我们省去人工标注</h4>\n\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\n\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\n\n<h4 id="6用原始的任务训练出来的模型能迁移去解决新任务吗">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\n\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\n\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\n\n<h4 id="7从理解到生成nlp-是最直面-aigc-最硬核难题的领域">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\n\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\n\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\n\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\n\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\n\n<h4 id="8数据和算力有了还不够">8、数据和算力有了,还不够</h4>\n\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\n\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\n\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\n\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\n\n<h3 id="二学术里程碑几篇重量级论文">二、学术里程碑:几篇重量级论文</h3>\n\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\n\n<h4 id="1提出-transformer-的attention-is-all-you-need2017">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\n\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\n\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\n\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\n\n<h4 id="2elmo-deep-contextualized-word-representations">2、ELMo: Deep contextualized word representations</h4>\n\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\n\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\n\n<h4 id="3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\n\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\n\n<ul>\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\n <li>在预训练中引入自监督学习任务。</li>\n</ul>\n\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href="https://www.mikecaptain.com/2022/12/17/ai-bert-1/">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\n\n<h4 id="4gpt-3-language-models-are-few-shot-learners2020">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\n\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\n\n<h4 id="其他的重量级论文">其他的重量级论文</h4>\n\n<ul>\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\n <li>……</li>\n</ul>\n\n<h3 id="三行业里程碑">三、行业里程碑</h3>\n\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\n\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\n\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\n\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\n\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\n\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\n\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\n\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\n\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\n\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\n\n<h3 id="四成本">四、成本</h3>\n\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\n\n<ul>\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\n</ul>\n\n<h3 id="五业内应用">五、业内应用</h3>\n\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\n\n<ul>\n <li>虚拟客服(可以乱真的)</li>\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\n <li>智能翻译</li>\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\n <li>AI 广告公司:替代传统广告公司</li>\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\n</ul>\n\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\n\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\n\n<ul>\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\n</ul>\n\n<p><img src="/img/src/2022-12-24-captain-nlp-7.png" alt="image" /></p>\n\n<ul>\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\n</ul>\n\n<p><img src="/img/src/2022-12-24-captain-nlp-2.jpg" alt="image" /></p>\n\n<ul>\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\n</ul>\n\n<p><img src="/img/src/2022-12-24-captain-nlp-8.png" alt="image" /></p>\n\n<ul>\n <li>2021 年年底,西湖心辰公司发布「<a href="https://www.heyfriday.cn/">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\n</ul>\n\n<p><img src="/img/src/2022-12-24-captain-nlp-1.png" alt="image" /></p>\n\n<h3 id="五行业内哪些人的言论值得我们日常重点关注">五、行业内哪些人的言论值得我们日常重点关注</h3>\n\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\n\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\n\n<blockquote>\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\n</blockquote>\n\n<blockquote>\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\n</blockquote>\n\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\n\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\n\n<blockquote>\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\n</blockquote>\n\n<blockquote>\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\n</blockquote>\n\n<h3 id="reference">Reference</h3>\n\n<ol>\n <li>https://beta.openai.com/docs/models</li>\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\n <li>https://hub.baai.ac.cn/view/21726</li>\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\n <li>https://www.sohu.com/a/615541698_121255906</li>\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\n</ol></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"ai\" /><category term=\"AI\" /><category term=\"人工智能\" /><category term=\"NLP\" /><category term=\"自然语言处理\" /><summary type=\"html\">火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。</summary></entry><entry><title type=\"html\">你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2022/12/17/ai-bert-1/\" rel=\"alternate\" type=\"text/html\" title=\"你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例\" /><published>2022-12-17T15:08:01+00:00</published><updated>2022-12-17T15:08:01+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2022/12/17/ai-bert-1</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2022/12/17/ai-bert-1/\"><p><strong>本文目录</strong></p>\n<ul id="markdown-toc">\n <li><a href="#一关于-bert-的一些背景" id="markdown-toc-一关于-bert-的一些背景">一、关于 BERT 的一些背景</a></li>\n <li><a href="#二开始一个-bert-的动手小试验" id="markdown-toc-二开始一个-bert-的动手小试验">二、开始一个 BERT 的动手小试验</a> <ul>\n <li><a href="#1安装-anaconda-来为部署-bert-做环境准备" id="markdown-toc-1安装-anaconda-来为部署-bert-做环境准备">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\n <li><a href="#2安装-bert-所需要的各种依赖" id="markdown-toc-2安装-bert-所需要的各种依赖">2、安装 BERT 所需要的各种依赖</a></li>\n <li><a href="#3下载一个预训练pre-train过的-bert-模型" id="markdown-toc-3下载一个预训练pre-train过的-bert-模型">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\n <li><a href="#5启动-bert-服务端" id="markdown-toc-5启动-bert-服务端">5、启动 BERT 服务端</a></li>\n <li><a href="#6在-pycharm-中使用-conda-的环境" id="markdown-toc-6在-pycharm-中使用-conda-的环境">6、在 PyCharm 中使用 Conda 的环境</a></li>\n <li><a href="#7编写程序实现-bert-客户端" id="markdown-toc-7编写程序实现-bert-客户端">7、编写程序实现 BERT 客户端</a></li>\n </ul>\n </li>\n <li><a href="#三bert-模型的优劣势及其原因" id="markdown-toc-三bert-模型的优劣势及其原因">三、BERT 模型的优劣势及其原因</a> <ul>\n <li><a href="#1bert-的优势是很明显的" id="markdown-toc-1bert-的优势是很明显的">1、BERT 的优势是很明显的</a> <ul>\n <li><a href="#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节" id="markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\n <li><a href="#12识别并专注于较重要的部分进行文本处理" id="markdown-toc-12识别并专注于较重要的部分进行文本处理">1.2、识别并专注于较重要的部分进行文本处理</a></li>\n <li><a href="#13快速构建针对具体任务的-nlp-系统" id="markdown-toc-13快速构建针对具体任务的-nlp-系统">1.3、快速构建针对具体任务的 NLP 系统</a></li>\n </ul>\n </li>\n <li><a href="#2bert-模型的劣势及其原因" id="markdown-toc-2bert-模型的劣势及其原因">2、BERT 模型的劣势及其原因</a> <ul>\n <li><a href="#21随机挖-mask-的完形填空题是有隐患的" id="markdown-toc-21随机挖-mask-的完形填空题是有隐患的">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\n <li><a href="#22nsp-任务有必要吗" id="markdown-toc-22nsp-任务有必要吗">2.2、NSP 任务有必要吗?</a></li>\n <li><a href="#23针对两个或以上词组成的连续词的词义被丢失" id="markdown-toc-23针对两个或以上词组成的连续词的词义被丢失">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\n <li><a href="#24需要的算力高" id="markdown-toc-24需要的算力高">2.4、需要的算力高</a></li>\n <li><a href="#25需要的模型大" id="markdown-toc-25需要的模型大">2.5、需要的模型大</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href="#四一些关于-bert-的问题" id="markdown-toc-四一些关于-bert-的问题">四、一些关于 BERT 的问题</a> <ul>\n <li><a href="#1bert-模型的所谓双向与-bilstm-的双向是啥区别" id="markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\n <li><a href="#2为什么-bert-可以比-rnn-更好地并行化" id="markdown-toc-2为什么-bert-可以比-rnn-更好地并行化">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\n </ul>\n </li>\n <li><a href="#reference" id="markdown-toc-reference">Reference</a></li>\n</ul>\n\n<h3 id="一关于-bert-的一些背景">一、关于 BERT 的一些背景</h3>\n\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\n\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\n\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\n\n<h3 id="二开始一个-bert-的动手小试验">二、开始一个 BERT 的动手小试验</h3>\n\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\n\n<h4 id="1安装-anaconda-来为部署-bert-做环境准备">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\n\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\n\n<p>更新 conda 到最新版本:</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>conda update <span class="nt">-n</span> base conda\n</code></pre></div></div>\n\n<p>使用 Python 3.7 创建一个新的环境:</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>conda create <span class="nt">-n</span> py37 <span class="nv">python</span><span class="o">=</span>3.7\n</code></pre></div></div>\n\n<p>激活这个新环境:</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>conda activate py37\n</code></pre></div></div>\n\n<p>验证正在使用的是正确版本的 Python</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">--version</span>\n</code></pre></div></div>\n\n<p>另外你可能还会用到的 conda 命令有:</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\nconda deactivate py37\n\n<span class="c"># 查看 conda 当前安装的所有库</span>\nconda list\n</code></pre></div></div>\n\n<h4 id="2安装-bert-所需要的各种依赖">2、安装 BERT 所需要的各种依赖</h4>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>conda <span class="nb">install </span><span class="nv">tensorflow</span><span class="o">==</span>1.14.0\n</code></pre></div></div>\n\n<p>验证 tensorflow 是否安装正确:</p>\n\n<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">tensorflow</span> <span class="k">as</span> <span class="n">tf</span>\n<span class="k">print</span><span class="p">(</span><span class="n">tf</span><span class="p">.</span><span class="n">__version__</span><span class="p">)</span>\n</code></pre></div></div>\n\n<h4 id="3下载一个预训练pre-train过的-bert-模型">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\n\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\n\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\n\n<ul>\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n</ul>\n\n<p>4、安装 BERT 的服务端和客户端</p>\n\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\n\n<p>在你激活的环境里,安装 <code class="language-plaintext highlighter-rouge">bert-as-service</code>:</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装服务端和客户端</span>\n<span class="c"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\nconda <span class="nb">install </span>bert-serving-server bert-serving-client \n验证 bert-as-service 是否安装成功\nbert-serving-start <span class="nt">-h</span>\n</code></pre></div></div>\n\n<h4 id="5启动-bert-服务端">5、启动 BERT 服务端</h4>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 命令行下启动BERT服务</span>\n<span class="c"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\nbert-serving-start <span class="nt">-model_dir</span> /模型/的/绝对/路径 <span class="nt">-num_worker</span><span class="o">=</span>4\n</code></pre></div></div>\n\n<h4 id="6在-pycharm-中使用-conda-的环境">6、在 PyCharm 中使用 Conda 的环境</h4>\n\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\n\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class="language-plaintext highlighter-rouge">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\n\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\n\n<h4 id="7编写程序实现-bert-客户端">7、编写程序实现 BERT 客户端</h4>\n\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\n\n<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">bert_serving.client</span> <span class="kn">import</span> <span class="n">BertClient</span>\n<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>\n\n<span class="c1"># 定义类\n</span><span class="k">class</span> <span class="nc">BertModel</span><span class="p">:</span>\n <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>\n <span class="k">try</span><span class="p">:</span>\n <span class="bp">self</span><span class="p">.</span><span class="n">bert_client</span> <span class="o">=</span> <span class="n">BertClient</span><span class="p">(</span><span class="n">ip</span><span class="o">=</span><span class="s">'127.0.0.1'</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">5555</span><span class="p">,</span> <span class="n">port_out</span><span class="o">=</span><span class="mi">5556</span><span class="p">)</span> <span class="c1"># 创建客户端对象\n</span> <span class="c1"># 注意:可以参考API,查看其它参数的设置\n</span> <span class="c1"># 127.0.0.1 表示本机IP,也可以用localhost\n</span> <span class="k">except</span><span class="p">:</span>\n <span class="k">raise</span> <span class="nb">Exception</span><span class="p">(</span><span class="s">"cannot create BertClient"</span><span class="p">)</span>\n\n <span class="k">def</span> <span class="nf">close_bert</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>\n <span class="bp">self</span><span class="p">.</span><span class="n">bert_client</span><span class="p">.</span><span class="n">close</span><span class="p">()</span> <span class="c1"># 关闭服务\n</span>\n <span class="k">def</span> <span class="nf">sentence_embedding</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span>\n <span class="s">'''对输入文本进行embedding\n Args:\n text: str, 输入文本\n Returns:\n text_vector: float, 返回一个列表,包含text的embedding编码值\n '''</span>\n <span class="n">text_vector</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">bert_client</span><span class="p">.</span><span class="n">encode</span><span class="p">([</span><span class="n">text</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span>\n <span class="k">return</span> <span class="n">text_vector</span> <span class="c1"># 获取输出结果\n</span>\n <span class="k">def</span> <span class="nf">caculate_similarity</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">vec_1</span><span class="p">,</span> <span class="n">vec_2</span><span class="p">):</span>\n <span class="s">'''根据两个语句的vector,计算它们的相似性\n Args:\n vec_1: float, 语句1的vector\n vec_2: float, 语句2的vector\n Returns:\n sim_value: float, 返回相似性的计算值\n '''</span>\n <span class="c1"># 根据cosine的计算公式\n</span> <span class="n">v1</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">mat</span><span class="p">(</span><span class="n">vec_1</span><span class="p">)</span>\n <span class="n">v2</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">mat</span><span class="p">(</span><span class="n">vec_2</span><span class="p">)</span>\n <span class="n">a</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">v1</span> <span class="o">*</span> <span class="n">v2</span><span class="p">.</span><span class="n">T</span><span class="p">)</span>\n <span class="n">b</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linalg</span><span class="p">.</span><span class="n">norm</span><span class="p">(</span><span class="n">v1</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">linalg</span><span class="p">.</span><span class="n">norm</span><span class="p">(</span><span class="n">v2</span><span class="p">)</span>\n <span class="n">cosine</span> <span class="o">=</span> <span class="n">a</span> <span class="o">/</span> <span class="n">b</span>\n <span class="k">return</span> <span class="n">cosine</span>\n\n\n<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>\n <span class="c1"># 创建bert对象\n</span> <span class="n">bert</span> <span class="o">=</span> <span class="n">BertModel</span><span class="p">()</span>\n <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>\n <span class="c1"># --- 输入语句 ----\n</span> <span class="n">input_a</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">'请输入语句1: '</span><span class="p">)</span>\n\n <span class="k">if</span> <span class="n">input_a</span> <span class="o">==</span> <span class="s">"N"</span> <span class="ow">or</span> <span class="n">input_a</span> <span class="o">==</span> <span class="s">"n"</span><span class="p">:</span>\n <span class="n">bert</span><span class="p">.</span><span class="n">close_bert</span><span class="p">()</span> <span class="c1"># 关闭服务\n</span> <span class="k">break</span>\n\n <span class="n">input_b</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">'请输入语句2: '</span><span class="p">)</span>\n\n <span class="c1"># --- 对输入语句进行embedding ---\n</span>\n <span class="n">a_vec</span> <span class="o">=</span> <span class="n">bert</span><span class="p">.</span><span class="n">sentence_embedding</span><span class="p">(</span><span class="n">input_a</span><span class="p">)</span>\n <span class="k">print</span><span class="p">(</span><span class="s">'a_vec shape : '</span><span class="p">,</span> <span class="n">a_vec</span><span class="p">.</span><span class="n">shape</span><span class="p">)</span>\n\n <span class="n">b_vec</span> <span class="o">=</span> <span class="n">bert</span><span class="p">.</span><span class="n">sentence_embedding</span><span class="p">(</span><span class="n">input_b</span><span class="p">)</span>\n <span class="k">print</span><span class="p">(</span><span class="s">'b_vec shape : '</span><span class="p">,</span> <span class="n">b_vec</span><span class="p">.</span><span class="n">shape</span><span class="p">)</span>\n\n <span class="c1"># 计算两个语句的相似性\n</span> <span class="n">cos</span> <span class="o">=</span> <span class="n">bert</span><span class="p">.</span><span class="n">caculate_similarity</span><span class="p">(</span><span class="n">a_vec</span><span class="p">,</span> <span class="n">b_vec</span><span class="p">)</span>\n <span class="k">print</span><span class="p">(</span><span class="s">'cosine value : '</span><span class="p">,</span> <span class="n">cos</span><span class="p">)</span>\n\n <span class="k">print</span><span class="p">(</span><span class="s">'</span><span class="se">\\n\\n</span><span class="s">'</span><span class="p">)</span>\n\n <span class="c1"># 如果相似性值大于0.85,则输出相似,否则,输出不同\n</span> <span class="k">if</span> <span class="n">cos</span> <span class="o">&gt;</span> <span class="mf">0.85</span><span class="p">:</span>\n <span class="k">print</span><span class="p">(</span><span class="s">"2个语句的含义相似"</span><span class="p">)</span>\n <span class="k">else</span><span class="p">:</span>\n <span class="k">print</span><span class="p">(</span><span class="s">"不相似"</span><span class="p">)</span>\n</code></pre></div></div>\n\n<p>在使用 <code class="language-plaintext highlighter-rouge">bert-serving-client</code> 连接 <code class="language-plaintext highlighter-rouge">bert-serving-server</code> 时,你需要确保 <code class="language-plaintext highlighter-rouge">bert-serving-server</code> 使用的模型和 <code class="language-plaintext highlighter-rouge">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\n\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>请输入语句1: \n请输入语句2: \n</code></pre></div></div>\n\n<p>两句输入好确认后,得到如下形式的结果:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a_vec shape : (768,)\nb_vec shape : (768,)\ncosine value : 0.8691698561422959\n</code></pre></div></div>\n\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\n\n<h3 id="三bert-模型的优劣势及其原因">三、BERT 模型的优劣势及其原因</h3>\n\n<p>论文地址:<a href="https://arxiv.org/abs/1810.04805">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\n\n<h4 id="1bert-的优势是很明显的">1、BERT 的优势是很明显的</h4>\n\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\n\n<blockquote>\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\n</blockquote>\n\n<h5 id="11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\n\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\n\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\n\n<h5 id="12识别并专注于较重要的部分进行文本处理">1.2、识别并专注于较重要的部分进行文本处理</h5>\n\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\n\n<h5 id="13快速构建针对具体任务的-nlp-系统">1.3、快速构建针对具体任务的 NLP 系统</h5>\n\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\n\n<h4 id="2bert-模型的劣势及其原因">2、BERT 模型的劣势及其原因</h4>\n\n<h5 id="21随机挖-mask-的完形填空题是有隐患的">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\n\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\n\n<h5 id="22nsp-任务有必要吗">2.2、NSP 任务有必要吗?</h5>\n\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\n\n<h5 id="23针对两个或以上词组成的连续词的词义被丢失">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\n\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\n\n<h5 id="24需要的算力高">2.4、需要的算力高</h5>\n\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\n\n<h5 id="25需要的模型大">2.5、需要的模型大</h5>\n\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\n\n<h3 id="四一些关于-bert-的问题">四、一些关于 BERT 的问题</h3>\n\n<h4 id="1bert-模型的所谓双向与-bilstm-的双向是啥区别">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\n\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\n\n<h4 id="2为什么-bert-可以比-rnn-更好地并行化">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\n\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\n\n<h3 id="reference">Reference</h3>\n\n<ol>\n <li>https://arxiv.org/abs/1810.04805</li>\n <li>https://github.com/google-research/bert</li>\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\n</ol></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"ai\" /><category term=\"BERT\" /><category term=\"AI\" /><category term=\"人工智能\" /><summary type=\"html\">2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。</summary></entry><entry><title type=\"html\">动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2022/12/11/wechat-chatgpt/\" rel=\"alternate\" type=\"text/html\" title=\"动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天\" /><published>2022-12-11T15:59:57+00:00</published><updated>2022-12-11T15:59:57+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2022/12/11/wechat-chatgpt</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2022/12/11/wechat-chatgpt/\"><p><img src="/img/src/2022-12-11-wechat-chatgpt-3.png" alt="image" /></p>\n\n<h3 id="写在前面">写在前面</h3>\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\n\n<h3 id="stepbystep">Step by step</h3>\n\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\n\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\n\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\n\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\n\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\n\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\n\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">chatGPTAccountPool</span><span class="pi">:</span>\n <span class="pi">-</span> <span class="na">email</span><span class="pi">:</span> <span class="s">&lt;your email&gt;</span>\n <span class="na">password</span><span class="pi">:</span> <span class="s">&lt;your password&gt;</span>\n<span class="c1"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\n<span class="na">chatPrivateTiggerKeyword</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>\n</code></pre></div></div>\n\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker pull holegots/wechat-chatgpt:latest。\n</code></pre></div></div>\n\n<p>第八步,启动 wechat-chatgpt:</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">-d</span> <span class="nt">--name</span> wechat-chatgpt <span class="nt">-v</span> <span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\n</code></pre></div></div>\n\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class="language-plaintext highlighter-rouge">npm install &amp;&amp; poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\n\n<table>\n <thead>\n <tr>\n <th><img src="/img/src/2022-12-11-wechat-chatgpt-1.png" alt="image" style="width:100%" /></th>\n <th><img src="/img/src/2022-12-11-wechat-chatgpt-2.png" alt="image" style="width:100%" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\n\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker logs <span class="nt">-f</span> wechat-chatgpt\n</code></pre></div></div>\n\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\n\n<p>如果要停止运行,用如下命令:</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker stop wechat-chatgpt\n</code></pre></div></div>\n\n<h3 id="参考">参考</h3>\n\n<p>1、<a href="https://github.com/fuergaosi233/wechat-chatgpt/tree/main">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"ai\" /><category term=\"人工智能\" /><category term=\"AI\" /><category term=\"ChatGPT\" /><category term=\"OpenAI\" /><category term=\"微信\" /><summary type=\"html\">最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作 ……</summary></entry><entry><title type=\"html\">确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2022/11/30/midjourney-first-test/\" rel=\"alternate\" type=\"text/html\" title=\"确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写\" /><published>2022-11-30T15:12:03+00:00</published><updated>2022-11-30T15:12:03+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2022/11/30/midjourney-first-test</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2022/11/30/midjourney-first-test/\"><p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src="/img/src/2022-12-16-midjourney-first-test-1.png" alt="image" /></th>\n <th><img src="/img/src/2022-12-16-midjourney-first-test-2.png" alt="image" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src="/img/src/2022-12-16-midjourney-first-test-3.png" alt="image" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"ai\" /><category term=\"AI\" /><category term=\"人工智能\" /><category term=\"diffusion\" /><category term=\"MidJourney\" /><category term=\"Text2Image\" /><category term=\"文生图\" /><category term=\"AIGC\" /><summary type=\"html\">因为 Diffusion 模型在计算机视觉领域的发展,可以说今年人工智能在计算机视觉领域大放异彩,各种 Text2Image 项目层出不穷,花了三分钟时间做了一组机甲图,确实非常惊艳 ……</summary></entry><entry><title type=\"html\">不要船开远了,就忘了为什么启航</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2022/08/11/captain-alibaba/\" rel=\"alternate\" type=\"text/html\" title=\"不要船开远了,就忘了为什么启航\" /><published>2022-08-11T15:53:57+00:00</published><updated>2022-08-11T15:53:57+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2022/08/11/captain-alibaba</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2022/08/11/captain-alibaba/\"><h3 id="写在前面">写在前面</h3>\n<p>偶然翻到 2020.06.11 刚来到阿里时写的一篇内容(我是 2020 年的 6 月 4 日我入职阿里巴巴集团),是有关于来阿里的期待、对这家公司的一些粗浅初步的理解。此时再翻来看看,最大的感触就是,提醒自己勿忘初心。</p>\n\n<p>在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:</p>\n\n<ul>\n <li>很多人是带着梦想来阿里的,那么我的梦想是什么呢?</li>\n <li>最喜欢新六脉的哪句话?为什么?</li>\n <li>关于阿里企业价值观:为什么要接受这套价值观?</li>\n <li>价值观的本质意义(极度务实视角)是什么?</li>\n <li>Landing 的 SOP</li>\n <li>问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</li>\n</ul>\n\n<p><img src="/img/src/2020-06-11-captain-alibaba-1.png" alt="image" /></p>\n\n<p>注:2020 年平安夜 · 百年湖畔 87 期合影</p>\n\n<h3 id="很多人是带着梦想来阿里的那么我的梦想是什么呢">很多人是带着梦想来阿里的,那么我的梦想是什么呢?</h3>\n\n<p>Christensen 在《创新者的窘境》中提到:每一次技术更迭,都需要破坏性创新,而破坏性创新在前一次技术更迭的胜出者内部是很难生长出来的。阿里诞生以来,不断地创造第二增长曲线:阿里巴巴、淘宝、支付宝、天猫、阿里云、钉钉 …… 这让我非常好奇。其中很多产品穿越多个时间周期,期间不断创造内生二次曲线。</p>\n\n<p>但是阿里也一样错失了很多,微信、美团、拼多多、抖/快…… 等等很多产品诞生在了其他公司,还有某些产品在不断的科技更迭中自身生长出了第二曲线。</p>\n\n<p>因此我来阿里的梦想也非常明确:<strong>参与或创造一次(甚至多次)第二曲线,可以是新产品,也可以是原有产品内生的。在这个过程中获得个人成长、个人价值。</strong></p>\n\n<p>一直以来,我有三个最想实现或得到的东西:LOVE、CREATION、FREEDOM。随着生活与工作的前行,对这三者的理解,在不断加深。在这个问题里,我想应该是讨论”CREATION”。</p>\n\n<p>CREATION 上,我的梦想的范式,大概是从自己中学时代就确立了,在某一次人类社会变革浪潮中,扮演有一定权重的角色。这里面有几个变量:<strong>什么领域(F)的变革;什么规模(S)的变革;多大的权重(W);什么角色(R)。</strong></p>\n\n<p>F 这个变量,我在中学及大学时代逐渐明确,是以相对普适的产品形式输出结果并对社会变革产生积极作用。后来越来越明确为科技与商业结合的领域。</p>\n\n<p>S、W 这两个变量,自然是越大越好。因此我会希望能够构建尽可能大的机会,或者参与到尽可能大的机会中。R 希望是有强烈 Ownership 的身份。</p>\n\n<p>因此过去几年我选择了创业。创业就像冲浪,你抓住一次浪并完成漂亮的动作,就是一次不算失败的创业。但是如果一个浪没抓住,你去追它是没意义的,而应该等待下一个浪。我认为在未来 5~10 年内难以出现规模能大到令我足够兴奋的科技浪潮。大浪潮中属于创业者的大机会很多,而中小浪潮的大机会基本只属于大平台,那么为了在壮年期做获得我的 CREATION,我选择了加入阿里这样的大平台。</p>\n\n<p>在最后做决定以及初来阿里的那个人生转折点,作为老阿里人的曲洋老师对我说的一句话,深深地鼓励了我,他说:”带着创业气质,把这里当你的舞台折腾!”</p>\n\n<p><img src="/img/src/2020-06-11-captain-alibaba-2.png" alt="image" /></p>\n\n<p>注:2020 年双十一 · 淘宝 KO</p>\n\n<h3 id="最喜欢新六脉的哪句话为什么">最喜欢新六脉的哪句话?为什么?</h3>\n\n<p>最喜欢的是“因为信任所以简单”。</p>\n\n<p>我一直认为人最重要的两个元特质是”真实”和“谦逊”,由”真实”可以塑造自我(对内)、构建信任(对外),后者可以带来清晰的边界,继而实现人与人之间高效的互动(这种互动包括各种人际关系在内,如婚姻、合伙、共事、合作等等)。</p>\n\n<p><strong>事物(虚实皆可)呈现在人的认知中,会得到三方面的投影:facts、opinion、feeling。如果我们足够真实,当我们需要把这三方面呈现给他人时,双方就能顺畅建立信任。信任的结果,就对应到这三方面:彼此之间建立共识(facts)、求同存异(opinion)、尊重感受(feeling),这就是”简单”。</strong></p>\n\n<p>另外一句是鼓励自己勇于绽放的一条:「此次此刻,非我莫属」。</p>\n\n<p>激情、自信、积极…… 通常行为统一表现为“勇于绽放自己”,绽放有表达(语言)与投身(行为)两种表现形式。更进一步推进就是”此次此刻,非我莫属”的阿里价值观。</p>\n\n<p>低调、稳重、谦逊,其实与“此次此刻,非我莫属“,并不矛盾。这点是我来到阿里后,发现自己在过去这些年的创业中已经不知不觉改变了,从 Introvert 逐渐变成了 Extrovert 的人,而且从曾经 social 中消耗能量,逐渐变为我现在可以感知到获得能量。这种变化,是我最近来阿里才确认发生的,此前因为自己创业者的身份没有察觉这种变化的发生。</p>\n\n<p><img src="/img/src/2020-06-11-captain-alibaba-3.png" alt="image" /></p>\n\n<p>注:2021 年秋 · 径山之行</p>\n\n<h3 id="关于阿里企业价值观为什么要接受这套价值观">关于阿里企业价值观:为什么要接受这套价值观?</h3>\n\n<p>马老师和老逍都提到这个:我们是寻找同路人,而不是教育别人。这其实非常明晰地解释了为什么阿里要构建一个毛细血管网络一样的政委体系。基于这种用人理念,政委体系不敢说是最优解,但一定是优解(而且是否有更优解的论证没有意义)。</p>\n\n<p>对于个人,我的理解是要做两件事:<strong>1)构建自己的价值观体系(初始化);2)寻找价值观契合的公司(做匹配)。这两点里,没有任何地方提到”你要改变价值观为了契合你所在的公司”。</strong></p>\n\n<p>而企业价值观呢,其实可以分两部分看待:普世价值观、独特价值观。前者因为是普世的,所以到了哪个公司这种价值观都对,这点老逍也提了,比如“客户第一”。后者是个性化的,但不存在孰高孰低,就像一个人内向还是外向,你不能说哪个是错的。</p>\n\n<p><img src="/img/src/2020-06-11-captain-alibaba-4.png" alt="image" /></p>\n\n<p>注:2021 年双十一 · 天天特卖团队</p>\n\n<h3 id="价值观的本质意义极度务实视角是什么">价值观的本质意义(极度务实视角)是什么?</h3>\n\n<p><strong>在充分考虑价值观适配使命、愿景基础上,价值观本身的意义,在和风细雨时(即企业价值观与其他价值判断相 match 时),是看不到的。但在暴风骤雨时(即企业价值观与其他价值判断相冲突时),就能显示其实实在在的作用了。</strong>我认为包括三类,前两个是阿里整体视角,第三个是阿里内部:</p>\n\n<ul>\n <li>经济体内,阿里与其他生态位的冲突或损益关系,如曾经的美蘑口一役。</li>\n <li>经济体内,其他的生态位之间的冲突或损益关系,如曾经的十月围城。</li>\n <li>阿里人的行为价值判断,如最近的钉钉代考事件、过往的各类廉政事件。</li>\n</ul>\n\n<p><img src="/img/src/2020-06-11-captain-alibaba-5.png" alt="image" /></p>\n\n<p>注:2021 年冬 · 淘宝天猫合并前合影</p>\n\n<h3 id="landing的sop">Landing 的 SOP</h3>\n\n<p>大家都说 landing 充满挑战,马老师其实给出了 landing 的 SOP 三部曲:<strong>一起打过仗、一起创过新、一起度过难。三个经历都 close 才算 smooth landing。</strong></p>\n\n<p>集团人才策略层面、HR 实操层面、Leader 层面、,对于新人 landing 能做到什么程度的保障,其实每个新人感受到的不尽相同:</p>\n\n<ul>\n <li>集团层面,始终是在构建更好的新人 landing 环境的,这符合自身价值,这能打下很好的底子。</li>\n <li>实操层面,包括面试阶段对候选人的价值观判断、预期管理,面试及入职后公司文化及人才体系的事实呈现、内化吸收和长期解惑。</li>\n <li>Leader 层面,这是新人体感最强烈的部分,也是最重要的部分。尽管拥抱变化,但首先 Leader 需要给出尽可能最全面的考虑,其次是对候选人的预期管理。好的 Leader 会给候选人提供合理的着陆点、多个降落伞、缓冲垫,完成 smooth landing。</li>\n</ul>\n\n<p><img src="/img/src/2020-06-11-captain-alibaba-6.png" alt="image" /></p>\n\n<p>注:2021 年夏 · 出差厦漳泉</p>\n\n<h3 id="问问自己来到阿里如果初期我可能需要做一点改变那会是什么">问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</h3>\n\n<p>曾经个人的激情与动力,常来自于“增长”。常说<strong>高增长掩盖一切</strong>,所以未来在阿里如果不能如创业般快速获得反馈得到积极结果,并且大平台中必然要接受大量关联方共同参与项目而导致的效率降低,因此我要逐渐改变自己,重新适应这种环境下的激情与动力获得方式。</p>\n\n<p>另一方面,作为创业公司的负责人,工作中鲜有因为内部原因而无法推进的事情,但是扮演肩部或腰部角色时,需要接受头部决策的一定程度不可控,这是我需要作出的适应与改变。关于这一点,我在几个月前就已经在做预期管理和心态调整,我认为以创业者的强适应性,这可能并不会是问题,但是我习惯于保持谨慎的乐观来面对自己。</p></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"thinking\" /><category term=\"思考\" /><summary type=\"html\">2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</summary></entry><entry><title type=\"html\">麦克船长的 Jekyll 快速教程</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2021/12/23/captains-jeckyll-learning/\" rel=\"alternate\" type=\"text/html\" title=\"麦克船长的 Jekyll 快速教程\" /><published>2021-12-23T19:43:02+00:00</published><updated>2021-12-23T19:43:02+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2021/12/23/captains-jeckyll-learning</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2021/12/23/captains-jeckyll-learning/\"><ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id="写在前面">写在前面</h3>\n\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\n\n<h3 id="part-1基本特点">Part 1、基本特点</h3>\n\n<h4 id="一基本语法">一、基本语法</h4>\n\n<ul>\n <li>变量:用双大括号表示变量 <code class="language-plaintext highlighter-rouge">麦克船长的技术、产品与商业博客</code></li>\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class="language-plaintext highlighter-rouge">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\n <li>支持循环与分支结构:比如 <code class="language-plaintext highlighter-rouge">for-endfor</code> 和 <code class="language-plaintext highlighter-rouge">if-elsif-else-endif</code> :可以使用 <code class="language-plaintext highlighter-rouge">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\n</ul>\n\n<h4 id="二典型-jekyll-项目结构及重要文件介绍">二、典型 Jekyll 项目结构及重要文件介绍</h4>\n\n<h5 id="1配置文件-_configyml">1、配置文件 <code class="language-plaintext highlighter-rouge">_config.yml</code></h5>\n\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class="language-plaintext highlighter-rouge">encoding: utf-8</code> 这一条。</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Site settings</span>\n<span class="na">encoding</span><span class="pi">:</span> <span class="s">utf-8</span>\n<span class="na">title</span><span class="pi">:</span> <span class="s">麦克船长的技术、产品与商业博客</span>\n<span class="na">description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管"</span>\n<span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://www.mikecaptain.com"</span>\n<span class="na">author</span><span class="pi">:</span>\n <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Your</span><span class="nv"> </span><span class="s">Name"</span>\n <span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://www.mikecaptian.com"</span>\n</code></pre></div></div>\n\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Markdown and highlighter</span>\n<span class="na">markdown</span><span class="pi">:</span> <span class="s">kramdown</span>\n<span class="na">highlighter</span><span class="pi">:</span> <span class="s">rouge</span>\n<span class="na">kramdown</span><span class="pi">:</span>\n <span class="na">input</span><span class="pi">:</span> <span class="s">GFM</span>\n <span class="na">syntax_highlighter</span><span class="pi">:</span> <span class="s">rouge</span>\n</code></pre></div></div>\n\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Plugins</span>\n<span class="na">plugins</span><span class="pi">:</span>\n <span class="pi">-</span> <span class="s">jekyll-paginate</span>\n <span class="pi">-</span> <span class="s">jekyll-sitemap</span>\n</code></pre></div></div>\n\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Build settings</span>\n<span class="na">baseurl</span><span class="pi">:</span> <span class="c1"># Change this to your relative path (ex: /blog/), or leave just a /</span>\n<span class="na">source</span><span class="pi">:</span> <span class="s">.</span>\n<span class="na">destination</span><span class="pi">:</span> <span class="s">./_site</span>\n<span class="na">permalink</span><span class="pi">:</span> <span class="s">/:title</span>\n<span class="na">paginate</span><span class="pi">:</span> <span class="m">20</span>\n<span class="na">paginate_path</span><span class="pi">:</span> <span class="s">/page:num/</span>\n<span class="na">collections</span><span class="pi">:</span>\n <span class="na">posts</span><span class="pi">:</span>\n <span class="na">output</span><span class="pi">:</span> <span class="no">true</span>\n <span class="na">permalink</span><span class="pi">:</span> <span class="s">/:year/:month/:day/:title/</span>\n <span class="na">directory</span><span class="pi">:</span> <span class="s">_posts</span>\n</code></pre></div></div>\n\n<h5 id="2布局文件_layouts-目录下的文件规则">2、布局文件:<code class="language-plaintext highlighter-rouge">_layouts</code> 目录下的文件规则</h5>\n\n<p>Jekyll 的 <code class="language-plaintext highlighter-rouge">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class="language-plaintext highlighter-rouge">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class="language-plaintext highlighter-rouge">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\n\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class="language-plaintext highlighter-rouge">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\n\n<h5 id="3页面文件及其头部">3、页面文件及其头部</h5>\n\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\n\n<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>\n<span class="na">layout</span><span class="pi">:</span> <span class="s">page</span>\n<span class="na">permalink</span><span class="pi">:</span> <span class="s">/categories/</span>\n<span class="na">title</span><span class="pi">:</span> <span class="s">Categories</span>\n<span class="nn">---</span>\n</code></pre></div></div>\n\n<p>每个页面文件的头部都会有layout,并与 <code class="language-plaintext highlighter-rouge">_layouts</code> 目录下的某个文件对应。</p>\n\n<h3 id="part-2jekyll-中的全局变量">Part 2、Jekyll 中的全局变量</h3>\n\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\n\n<ul>\n <li><code class="language-plaintext highlighter-rouge">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\n <li><code class="language-plaintext highlighter-rouge">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\n <li><code class="language-plaintext highlighter-rouge">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\n <li><code class="language-plaintext highlighter-rouge">content</code>:包含当前页面或文章的内容。</li>\n <li><code class="language-plaintext highlighter-rouge">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\n <li><code class="language-plaintext highlighter-rouge">tags</code>:包含有关网站的所有标签的信息。</li>\n <li><code class="language-plaintext highlighter-rouge">related_posts</code>:包含与当前文章有关的文章的信息。</li>\n</ul>\n\n<p>这些变量可以在模板中使用,比如:</p>\n\n<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;h1&gt;</span>{{ page.title }}<span class="nt">&lt;/h1&gt;</span>\n<span class="nt">&lt;p&gt;</span>{{ site.description }}<span class="nt">&lt;/p&gt;</span>\n<span class="nt">&lt;ul&gt;</span>\n {{ for category in site.categories %}\n <span class="nt">&lt;li&gt;</span>{{ category }}<span class="nt">&lt;/li&gt;</span>\n {{ endfor %}\n<span class="nt">&lt;/ul&gt;</span> \n</code></pre></div></div>\n\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class="language-plaintext highlighter-rouge">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">my_custom_variable</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Hello</span><span class="nv"> </span><span class="s">World"</span>\n</code></pre></div></div>\n\n<p>然后就可以在模板文件中使用 <code class="language-plaintext highlighter-rouge">site.my_custom_variable</code> 访问这个自定义变量了。</p>\n\n<h4 id="一site变量">一、site变量</h4>\n\n<p><code class="language-plaintext highlighter-rouge">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\n\n<h5 id="1sitecategories">1、<code class="language-plaintext highlighter-rouge">site.categories</code></h5>\n\n<p><code class="language-plaintext highlighter-rouge">site.categories</code> 是一个 array,每个元素取出它的 <code class="language-plaintext highlighter-rouge">first</code> 就是 <code class="language-plaintext highlighter-rouge">category</code> 的名字,如下使用:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\n</code></pre></div></div>\n\n<h5 id="2sitepages">2、site.pages</h5>\n\n<p><code class="language-plaintext highlighter-rouge">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class="language-plaintext highlighter-rouge">site.pages</code> 中包含的是整个网站中所有的页面。</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{{ for page in site.pages %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id="3其他常用属性">3、其他常用属性</h5>\n\n<ul>\n <li><code class="language-plaintext highlighter-rouge">site.title</code>:是网站的标题。</li>\n <li><code class="language-plaintext highlighter-rouge">site.related_posts</code>:相关文章的列表。</li>\n <li><code class="language-plaintext highlighter-rouge">site.data</code>:从 <code class="language-plaintext highlighter-rouge">_data</code> 目录加载的数据。</li>\n <li><code class="language-plaintext highlighter-rouge">site.static_files</code>:静态文件的列表。</li>\n <li><code class="language-plaintext highlighter-rouge">site.collections</code>:自定义集合的列表。</li>\n</ul>\n\n<h4 id="二page-变量">二、<code class="language-plaintext highlighter-rouge">page</code> 变量</h4>\n\n<p>在 Jekyll 中,<code class="language-plaintext highlighter-rouge">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class="language-plaintext highlighter-rouge">page</code> 变量属性包括:</p>\n\n<ul>\n <li><code class="language-plaintext highlighter-rouge">layout</code>:表示页面使用的布局模板的名称。</li>\n <li><code class="language-plaintext highlighter-rouge">title</code>:表示页面的标题。</li>\n <li><code class="language-plaintext highlighter-rouge">date</code>:表示页面的发布日期。</li>\n <li><code class="language-plaintext highlighter-rouge">categories</code>:表示页面所属的分类列表。</li>\n <li><code class="language-plaintext highlighter-rouge">tags</code>:表示页面所属的标签列表。</li>\n <li><code class="language-plaintext highlighter-rouge">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\n</ul>\n\n<p>在模板中,可以使用 <code class="language-plaintext highlighter-rouge">{{ page.属性名 }}</code> 的方式来访问 <code class="language-plaintext highlighter-rouge">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class="language-plaintext highlighter-rouge">{{ page.title }}</code>。此外,<code class="language-plaintext highlighter-rouge">page</code> 变量还有其他属性,如 <code class="language-plaintext highlighter-rouge">permalink</code>、<code class="language-plaintext highlighter-rouge">excerpt</code>、<code class="language-plaintext highlighter-rouge">url</code> 等,可以根据需要调用。</p>\n\n<h3 id="part-3控制结构">Part 3、控制结构</h3>\n\n<h4 id="1if-else-分支结构">1、<code class="language-plaintext highlighter-rouge">if-else</code> 分支结构</h4>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{{ if tmp_var == "type1" %}\n{{ elsif tmp_var == "type2" %}\n{{ elsif tmp_var == "type3" %}\n{{ elsif tmp_var == "type4" %}\n{{ else tmp_var == "type5" %}\n{{ endif %}\n</code></pre></div></div>\n\n<h4 id="2for-endfor-循环结构">2、<code class="language-plaintext highlighter-rouge">for-endfor</code> 循环结构</h4>\n\n<p>不带条件判断的 <code class="language-plaintext highlighter-rouge">for</code> 循环如下:</p>\n\n<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{{ for post in paginator.posts %}\n\t<span class="c">&lt;!-- Your other sentences --&gt;</span>\n{{ endfor %}\n</code></pre></div></div>\n\n<p>带条件循环的 <code class="language-plaintext highlighter-rouge">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\n\n<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{{ for page in site.pages | where: "dir", "categories" %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id="3jekyll-支持的其他结构包括">3、Jekyll 支持的其他结构包括:</h5>\n\n<ul>\n <li><code class="language-plaintext highlighter-rouge">case</code> 用于在多个可能的条件中执行代码的结构。</li>\n <li><code class="language-plaintext highlighter-rouge">capture</code> 用于捕获输出的结构。</li>\n <li><code class="language-plaintext highlighter-rouge">cycle</code> 用于循环一组字符串的结构。</li>\n <li><code class="language-plaintext highlighter-rouge">include</code> 用于包含其他文件的结构。</li>\n <li><code class="language-plaintext highlighter-rouge">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\n <li><code class="language-plaintext highlighter-rouge">while</code> 用于在满足指定条件时执行代码的结构。</li>\n</ul>\n\n<h3 id="参考">参考:</h3>\n\n<p>1、<a href="https://learn.cloudcannon.com/jekyll/list-posts-by-category/">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\n2、<a href="https://jekyllrb.com/docs/">https://jekyllrb.com/docs/</a></p></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"web\" /><category term=\"Jekyll\" /><category term=\"Web\" /><category term=\"前端\" /><summary type=\"html\">Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可 ……</summary></entry><entry><title type=\"html\">如何使用 Jekyll 基于 Github Pages 搭建个人博客</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2021/12/21/build-github-pages-with-jekyll/\" rel=\"alternate\" type=\"text/html\" title=\"如何使用 Jekyll 基于 Github Pages 搭建个人博客\" /><published>2021-12-21T15:53:57+00:00</published><updated>2021-12-21T15:53:57+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2021/12/21/build-github-pages-with-jekyll</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2021/12/21/build-github-pages-with-jekyll/\"><p><strong>本文目录</strong></p>\n<ul id="markdown-toc">\n <li><a href="#写在前面" id="markdown-toc-写在前面">写在前面</a></li>\n <li><a href="#1github上的准备" id="markdown-toc-1github上的准备">1、GitHub 上的准备</a></li>\n <li><a href="#2了解ruby和jekyll" id="markdown-toc-2了解ruby和jekyll">2、了解 Ruby 和 Jekyll</a></li>\n <li><a href="#3了解gem" id="markdown-toc-3了解gem">3、了解 Gem</a></li>\n <li><a href="#4安装homebrew" id="markdown-toc-4安装homebrew">4、安装 Homebrew</a></li>\n <li><a href="#5用homebrew安装ruby" id="markdown-toc-5用homebrew安装ruby">5、用 Homebrew 安装 Ruby</a></li>\n <li><a href="#6安装jekyll和bundler" id="markdown-toc-6安装jekyll和bundler">6、安装 Jekyll 和 Bundler</a></li>\n <li><a href="#7使用bundle管理包依赖关系" id="markdown-toc-7使用bundle管理包依赖关系">7、使用 bundle 管理包依赖关系</a></li>\n <li><a href="#8本地启动一下看看" id="markdown-toc-8本地启动一下看看">8、本地启动一下看看</a></li>\n <li><a href="#9用jekyll创建一个项目" id="markdown-toc-9用jekyll创建一个项目">9、用 Jekyll 创建一个项目</a></li>\n <li><a href="#10修改gemfile文件" id="markdown-toc-10修改gemfile文件">10、修改 Gemfile 文件</a></li>\n <li><a href="#11配置githubpages" id="markdown-toc-11配置githubpages">11、配置 Github Pages</a></li>\n <li><a href="#12配置一个jekylltheme" id="markdown-toc-12配置一个jekylltheme">12、配置一个 Jekyll Theme</a></li>\n <li><a href="#13设置自定义域名" id="markdown-toc-13设置自定义域名">13、设置自定义域名</a></li>\n <li><a href="#14用-rouge-实现代码高亮" id="markdown-toc-14用-rouge-实现代码高亮">14、用 rouge 实现代码高亮</a></li>\n <li><a href="#15一些扩展问题" id="markdown-toc-15一些扩展问题">15、一些扩展问题</a> <ul>\n <li><a href="#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢" id="markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\n <li><a href="#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面" id="markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\n <li><a href="#q3如何为每篇文章添加一个目录" id="markdown-toc-q3如何为每篇文章添加一个目录">Q3:如何为每篇文章添加一个目录</a></li>\n <li><a href="#q4如何在-jekyll-中支持-katex" id="markdown-toc-q4如何在-jekyll-中支持-katex">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\n <li><a href="#在-githubio-上" id="markdown-toc-在-githubio-上">在 GitHub.io 上</a></li>\n <li><a href="#如果不在-githubio-上则还需要额外工作" id="markdown-toc-如果不在-githubio-上则还需要额外工作">如果不在 GitHub.io 上,则还需要额外工作</a></li>\n <li><a href="#使用示例" id="markdown-toc-使用示例">使用示例</a></li>\n </ul>\n </li>\n <li><a href="#q5jekyll-中如何支持-graphviz-" id="markdown-toc-q5jekyll-中如何支持-graphviz-">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\n <li><a href="#q6如何显示--或者--" id="markdown-toc-q6如何显示--或者--">Q6:如何显示 <code class="language-plaintext highlighter-rouge">{%</code> 或者 <code class="language-plaintext highlighter-rouge">{{</code> ?</a></li>\n </ul>\n </li>\n <li><a href="#参考" id="markdown-toc-参考">参考</a></li>\n</ul>\n\n<h3 id="写在前面">写在前面</h3>\n\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\n\n<ul>\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\n</ul>\n\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\n\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\n\n<h3 id="1github上的准备">1、GitHub 上的准备</h3>\n\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/username/username.github.io\n</code></pre></div></div>\n\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git add <span class="nt">--all</span>\n<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"Initial commit"</span>\n<span class="nv">$ </span>git push <span class="nt">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id="2了解ruby和jekyll">2、了解 Ruby 和 Jekyll</h3>\n\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\n\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\n\n<h3 id="3了解gem">3、了解 Gem</h3>\n\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\n\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\ngem sources -l\n</code></pre></div></div>\n\n<h3 id="4安装homebrew">4、安装 Homebrew</h3>\n\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>/bin/bash <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="si">)</span><span class="s2">"</span>\n</code></pre></div></div>\n\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class="o">&gt;&gt;</span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class="o">&gt;&gt;</span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\n\n<h3 id="5用homebrew安装ruby">5、用 Homebrew 安装 Ruby</h3>\n\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew <span class="nb">install </span>chruby ruby-install xz\n</code></pre></div></div>\n\n<p>安装 Ruby 的最新版本:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ruby-install ruby\n</code></pre></div></div>\n\n<p>这时候提示如下问题:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> Updating ruby versions ...\n<span class="o">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class="se">\\</span>\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\n<span class="o">!!!</span> Failed to download ruby versions!\n</code></pre></div></div>\n\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ echo "185.199.111.133 raw.githubusercontent.com" &gt;&gt; /etc/hosts\n</code></pre></div></div>\n\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"source </span><span class="si">$(</span>brew <span class="nt">--prefix</span><span class="si">)</span><span class="s2">/opt/chruby/share/chruby/chruby.sh"</span> <span class="o">&gt;&gt;</span> ~/.zshrc\n<span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"source </span><span class="si">$(</span>brew <span class="nt">--prefix</span><span class="si">)</span><span class="s2">/opt/chruby/share/chruby/auto.sh"</span> <span class="o">&gt;&gt;</span> ~/.zshrc\n<span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"chruby ruby-3.1.2"</span> <span class="o">&gt;&gt;</span> ~/.zshrc <span class="c"># run 'chruby' to see actual version</span>\n</code></pre></div></div>\n\n<p>再看下 Ruby 版本对不对:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ruby <span class="nt">-v</span>\n</code></pre></div></div>\n\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\n\n<h3 id="6安装jekyll和bundler">6、安装 Jekyll 和 Bundler</h3>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gem <span class="nb">install </span>jekyll bundler\n</code></pre></div></div>\n\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\n\n<h3 id="7使用bundle管理包依赖关系">7、使用 bundle 管理包依赖关系</h3>\n\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source</span> <span class="s1">'https://rubygems.org'</span>\ngem <span class="s1">'nokogiri'</span>\ngem <span class="s1">'rack'</span>, <span class="s1">'~&gt; 2.2.4'</span>\ngem <span class="s1">'rspec'</span>\ngem <span class="s1">'jekyll'</span>\n</code></pre></div></div>\n\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bundle <span class="nb">install</span>\n</code></pre></div></div>\n\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git add Gemfile Gemfile.lock\n</code></pre></div></div>\n\n<h3 id="8本地启动一下看看">8、本地启动一下看看</h3>\n\n<p>先用 bundle 如下命令来启动:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bundle <span class="nb">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Configuration file: none\n Source: /Users/captain/Workspace/poechant.github.io\n Destination: /Users/captain/Workspace/poechant.github.io/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n done in 0.014 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\n Server address: http://127.0.0.1:4000\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src="/img/src/2022-12-21-build-github-pages-with-jekyll-1.png" alt="image" /></p>\n\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git pull <span class="nt">--no-rebase</span>\n<span class="nv">$ </span>git push <span class="nt">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id="9用jekyll创建一个项目">9、用 Jekyll 创建一个项目</h3>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>jekyll new CaptainMikeBlog\n<span class="nv">$ </span><span class="nb">cd </span>CaptainMikeBlog\n<span class="nv">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n Jekyll Feed: Generating feed for posts\n done in 0.365 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\n Server address: http://127.0.0.1:4000/\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src="/img/src/2022-12-21-build-github-pages-with-jekyll-2.png" alt="image" /></p>\n\n<h3 id="10修改gemfile文件">10、修改 Gemfile 文件</h3>\n\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gem <span class="s2">"github-pages"</span>, <span class="s2">"~&gt; GITHUB-PAGES-VERSION"</span>, group: :jekyll_plugins\n</code></pre></div></div>\n\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ bundle install\n</code></pre></div></div>\n\n<p>再本地启动服务器测试:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>得到如下提示:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\nPrepending <span class="sb">`</span>bundle <span class="nb">exec</span><span class="sb">`</span> to your <span class="nb">command </span>may solve this. <span class="o">(</span>Gem::LoadError<span class="o">)</span>\n</code></pre></div></div>\n\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bundle add webrick\n<span class="nv">$ </span>bundle <span class="nb">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\n\n<h3 id="11配置githubpages">11、配置 Github Pages</h3>\n\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>baseurl: ""\nurl: "http://your-username.github.io"\n</code></pre></div></div>\n\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\n\n<h3 id="12配置一个jekylltheme">12、配置一个 Jekyll Theme</h3>\n\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>_includes\n_layouts\n_sass\ncss\njs\nimg\n404.markdown\nindex.html\n</code></pre></div></div>\n\n<p>不同主题会有所不同,这里只列个大概。</p>\n\n<h3 id="13设置自定义域名">13、设置自定义域名</h3>\n\n<p>添加四条 A 记录,记录值如下:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>185.199.108.153\n185.199.109.153\n185.199.110.153\n185.199.111.153\n</code></pre></div></div>\n\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\n\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\n\n<h3 id="14用-rouge-实现代码高亮">14、用 rouge 实现代码高亮</h3>\n\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\n\n<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem install kramdom rouge\n</code></pre></div></div>\n\n<p>然后配置 _config.yml 文件:</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">markdown</span><span class="pi">:</span> <span class="s">kramdown</span>\n<span class="na">highlighter</span><span class="pi">:</span> <span class="s">rouge</span>\n\n<span class="na">kramdown</span><span class="pi">:</span>\n <span class="na">input</span><span class="pi">:</span> <span class="s">GFM</span>\n <span class="na">syntax_highlighter</span><span class="pi">:</span> <span class="s">rouge</span>\n</code></pre></div></div>\n\n<p>然后用 rouge 创建 syntax.css 文件:</p>\n\n<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rougify style github <span class="o">&gt;</span> css/syntax.css\n</code></pre></div></div>\n\n<p>在 <code class="language-plaintext highlighter-rouge">_include/head.html</code> 文件中添加:</p>\n\n<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"/css/syntax.css"</span> <span class="nt">/&gt;</span>\n</code></pre></div></div>\n\n<h3 id="15一些扩展问题">15、一些扩展问题</h3>\n\n<h4 id="q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\n\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\n\n<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>\n<span class="na">title</span><span class="pi">:</span> <span class="s">这是一篇文章</span>\n<span class="na">excerpt</span><span class="pi">:</span> <span class="s">这是文章的摘要</span>\n<span class="nn">---</span>\n\n这是文章的正文内容\n</code></pre></div></div>\n\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\n\n<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;ul&gt;</span>\n {% for post in paginator.posts %}\n <span class="nt">&lt;li&gt;</span>\n <span class="nt">&lt;h2&gt;&lt;a</span> <span class="na">href=</span><span class="s">"{{ post.url }}"</span><span class="nt">&gt;</span>{{ post.title }}<span class="nt">&lt;/a&gt;&lt;/h2&gt;</span>\n <span class="nt">&lt;p&gt;</span>{{ post.excerpt }}<span class="nt">&lt;/p&gt;</span>\n <span class="nt">&lt;/li&gt;</span>\n {% endfor %}\n<span class="nt">&lt;/ul&gt;</span>\n</code></pre></div></div>\n\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\n\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\n\n<h4 id="q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\n\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class="language-plaintext highlighter-rouge">_layouts</code>目录下创建一个<code class="language-plaintext highlighter-rouge">category.html</code>文件:</p>\n\n<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---\nlayout: default\n---\n\n<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;</span>\n <span class="nt">&lt;br&gt;</span>\n {% if site.categories[page.category] %}\n {% for post in site.categories[page.category] %}\n <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"{% if site.baseurl == "</span><span class="err">/"</span> <span class="err">%}{{</span> <span class="na">post.url</span> <span class="err">}}{%</span> <span class="na">else</span> <span class="err">%}{{</span> <span class="na">post.url</span> <span class="err">|</span> <span class="na">prepend:</span> <span class="na">site.baseurl</span> <span class="err">}}{%</span> <span class="na">endif</span> <span class="err">%}"</span><span class="nt">&gt;</span>\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\n <span class="nt">&lt;/a&gt;</span>\n {% endfor %}\n {% else %}\n <span class="nt">&lt;br&gt;</span>\n <span class="nt">&lt;p&gt;</span>No posts for this category. If you have something in mind, check <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"/write"</span><span class="nt">&gt;</span>Write For Us<span class="nt">&lt;/a&gt;</span>page.<span class="nt">&lt;/p&gt;</span>\n {% endif %}\n<span class="nt">&lt;/div&gt;</span>\n</code></pre></div></div>\n\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class="language-plaintext highlighter-rouge">_config.yml</code>文件:</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">include</span><span class="pi">:</span> <span class="pi">[</span><span class="s1">'</span><span class="s">_categories'</span><span class="pi">]</span>\n</code></pre></div></div>\n\n<p>在根目录创建一个<code class="language-plaintext highlighter-rouge">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class="language-plaintext highlighter-rouge">ai.html</code>如下:</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>\n<span class="na">layout</span><span class="pi">:</span> <span class="s">category</span>\n<span class="na">title</span><span class="pi">:</span> <span class="s">人工智能</span>\n<span class="na">description</span><span class="pi">:</span> <span class="s">This is the description.</span>\n<span class="na">permalink</span><span class="pi">:</span> <span class="s">/category/ai</span>\n<span class="na">category</span><span class="pi">:</span> <span class="s">ai</span>\n<span class="na">category_type</span><span class="pi">:</span> <span class="s">tech</span>\n<span class="nn">---</span>\n</code></pre></div></div>\n\n<p>那么之后每次创建文件时,在头部写<code class="language-plaintext highlighter-rouge">category</code>一定要与这些<code class="language-plaintext highlighter-rouge">categories</code>中的<code class="language-plaintext highlighter-rouge">html</code>文件对应起来。</p>\n\n<h4 id="q3如何为每篇文章添加一个目录">Q3:如何为每篇文章添加一个目录</h4>\n\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\n\n<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">*</span> TOC\n{:toc}\n</code></pre></div></div>\n\n<h4 id="q4如何在-jekyll-中支持-katex">Q4:如何在 Jekyll 中支持 KaTeX</h4>\n\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\n\n<h5 id="在-githubio-上">在 GitHub.io 上</h5>\n\n<p>先修改 <code class="language-plaintext highlighter-rouge">_config.yml</code>:</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">kramdown</span><span class="pi">:</span>\n <span class="na">math_engine</span><span class="pi">:</span> <span class="s">katex</span>\n</code></pre></div></div>\n\n<p>然后修改 <code class="language-plaintext highlighter-rouge">_includes/head.html</code> 文件,在 <code class="language-plaintext highlighter-rouge">&lt;head&gt;</code> 与 <code class="language-plaintext highlighter-rouge">&lt;/head&gt;</code> 中间:</p>\n\n<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!--KaTeX--&gt;</span>\n <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span>\n <span class="na">href=</span><span class="s">"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css"</span>\n <span class="na">integrity=</span><span class="s">"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X"</span>\n <span class="na">crossorigin=</span><span class="s">"anonymous"</span><span class="nt">&gt;</span>\n <span class="nt">&lt;script </span><span class="na">defer</span>\n <span class="na">src=</span><span class="s">"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js"</span>\n <span class="na">integrity=</span><span class="s">"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4"</span>\n <span class="na">crossorigin=</span><span class="s">"anonymous"</span><span class="nt">&gt;&lt;/script&gt;</span>\n <span class="nt">&lt;script </span><span class="na">defer</span>\n <span class="na">src=</span><span class="s">"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js"</span>\n <span class="na">integrity=</span><span class="s">"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa"</span>\n <span class="na">crossorigin=</span><span class="s">"anonymous"</span><span class="nt">&gt;&lt;/script&gt;</span>\n <span class="nt">&lt;script&gt;</span>\n <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">DOMContentLoaded</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>\n <span class="nx">renderMathInElement</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">,</span> <span class="p">{</span>\n <span class="c1">// ...options...</span>\n <span class="p">});</span>\n <span class="p">});</span>\n <span class="nt">&lt;/script&gt;</span>\n</code></pre></div></div>\n\n<h5 id="如果不在-githubio-上则还需要额外工作">如果不在 GitHub.io 上,则还需要额外工作</h5>\n\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>kramdom-math-katex\n\ngem <span class="nb">install </span>katex\ngem <span class="nb">install </span>execjs\n\ngem <span class="nb">install </span>therubyracer\ngem <span class="nb">install </span>therubyrhino\ngem <span class="nb">install </span>duktape\n</code></pre></div></div>\n\n<h5 id="使用示例">使用示例</h5>\n\n<p>以如下方式输入输入如下内容:</p>\n\n<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{% raw %}\n$$ \\sum_{i=1}^{n} a_i $$\n{% endraw %}\n</code></pre></div></div>\n\n<p>就会得到一个数学公式:</p>\n\n\\[\\sum_{i=1}^{n} a_i\\]\n\n<h4 id="q5jekyll-中如何支持-graphviz-">Q5:Jekyll 中如何支持 Graphviz ?</h4>\n\n<p>这要依赖 <code class="language-plaintext highlighter-rouge">jekyll-graphviz-dot</code>,修改 <code class="language-plaintext highlighter-rouge">Gemfile</code> 增加一句:</p>\n\n<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>group :jekyll_plugins <span class="k">do\n </span>gem <span class="s2">"jekyll-graphviz-dot"</span>\nend\n</code></pre></div></div>\n\n<p>再修改 <code class="language-plaintext highlighter-rouge">_config.yml</code> 配置文件:</p>\n\n<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">plugins</span><span class="pi">:</span>\n <span class="pi">-</span> <span class="s">jekyll-graphviz</span>\n</code></pre></div></div>\n\n<p>再在本地安装 graphviz,可以通过 <code class="language-plaintext highlighter-rouge">conda install graphviz</code> 或者 <code class="language-plaintext highlighter-rouge">brew install graphviz</code>。然后 <code class="language-plaintext highlighter-rouge">bundle install</code> 再 <code class="language-plaintext highlighter-rouge">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\n\n<pre><code class="language-graphviz">{% graph some graph title %}\ndigraph G {\n a -&gt; b\n b -&gt; c\n c -&gt; a\n}\n{% endgraph %}\n</code></pre>\n\n<p>如果看到如下效果,就说明你都配置成功了:</p>\n\n<div class="graphviz-wrapper">\n\n<!-- Generated by graphviz version 2.43.0 (0)\n -->\n<!-- Title: G Pages: 1 -->\n<svg role="img" aria-label="some graph title" width="89pt" height="188pt" viewBox="0.00 0.00 89.00 188.00">\n<title>some graph title</title>\n<desc>\ndigraph G {\n a -&gt; b\n b -&gt; c\n c -&gt; a\n}\n</desc>\n\n<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 184)">\n<title>G</title>\n<polygon fill="white" stroke="transparent" points="-4,4 -4,-184 85,-184 85,4 -4,4" />\n<!-- a -->\n<g id="node1" class="node">\n<title>a</title>\n<ellipse fill="none" stroke="black" cx="54" cy="-162" rx="27" ry="18" />\n<text text-anchor="middle" x="54" y="-158.3" font-family="Times,serif" font-size="14.00">a</text>\n</g>\n<!-- b -->\n<g id="node2" class="node">\n<title>b</title>\n<ellipse fill="none" stroke="black" cx="27" cy="-90" rx="27" ry="18" />\n<text text-anchor="middle" x="27" y="-86.3" font-family="Times,serif" font-size="14.00">b</text>\n</g>\n<!-- a&#45;&gt;b -->\n<g id="edge1" class="edge">\n<title>a&#45;&gt;b</title>\n<path fill="none" stroke="black" d="M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35" />\n<polygon fill="black" stroke="black" points="40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03" />\n</g>\n<!-- c -->\n<g id="node3" class="node">\n<title>c</title>\n<ellipse fill="none" stroke="black" cx="54" cy="-18" rx="27" ry="18" />\n<text text-anchor="middle" x="54" y="-14.3" font-family="Times,serif" font-size="14.00">c</text>\n</g>\n<!-- b&#45;&gt;c -->\n<g id="edge2" class="edge">\n<title>b&#45;&gt;c</title>\n<path fill="none" stroke="black" d="M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35" />\n<polygon fill="black" stroke="black" points="47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55" />\n</g>\n<!-- c&#45;&gt;a -->\n<g id="edge3" class="edge">\n<title>c&#45;&gt;a</title>\n<path fill="none" stroke="black" d="M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99" />\n<polygon fill="black" stroke="black" points="56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44" />\n</g>\n</g>\n</svg>\n</div>\n\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\n\n<h4 id="q6如何显示--或者--">Q6:如何显示 <code class="language-plaintext highlighter-rouge">{%</code> 或者 <code class="language-plaintext highlighter-rouge">{{</code> ?</h4>\n\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class="language-plaintext highlighter-rouge">{% raw %}</code> 和 <code class="language-plaintext highlighter-rouge">{% endraw %}</code> 呢?方法如下:</p>\n\n<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{% raw %}{%{% endraw %} raw %}\n{% raw %}{%{% endraw %} endraw %}\n</code></pre></div></div>\n\n<p>如上,就是用 <code class="language-plaintext highlighter-rouge">{% raw %}</code> 和 <code class="language-plaintext highlighter-rouge">{% endraw %}</code> 把 <code class="language-plaintext highlighter-rouge">{%</code> 包起来,但是 <code class="language-plaintext highlighter-rouge">%}</code> 不用包。应该讲的很清楚了吧。</p>\n\n<h3 id="参考">参考</h3>\n\n<ol>\n <li><a href="https://bundler.io">https://bundler.io</a></li>\n <li><a href="https://jekyllrb.com/docs/">https://jekyllrb.com/docs/</a></li>\n <li><a href="https://zhuanlan.zhihu.com/p/87225594">https://zhuanlan.zhihu.com/p/87225594</a></li>\n <li><a href="https://chat.openai.com/chat">https://chat.openai.com/chat</a></li>\n <li><a href="https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\n <li><a href="https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\n <li><a href="https://github.com/dyutibarma/monochrome">https://github.com/dyutibarma/monochrome</a></li>\n <li><a href="https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\n <li><a href="http://www.seanbuscay.com/blog/jekyll-toc-markdown/">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\n <li><a href="https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\n <li><a href="https://github.com/DerekStride/jekyll-graphviz">https://github.com/DerekStride/jekyll-graphviz</a></li>\n <li><a href="https://github.com/DerekStride/jekyll-graphviz">https://github.com/DerekStride/jekyll-graphviz</a></li>\n</ol></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"web\" /><category term=\"Jekyll\" /><category term=\"Github Pages\" /><category term=\"前端\" /><summary type=\"html\">GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客 ……</summary></entry><entry><title type=\"html\">欢迎成为「淘宝-天天特卖」团队的创业合伙人!</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2021/11/11/captain-tttm/\" rel=\"alternate\" type=\"text/html\" title=\"欢迎成为「淘宝-天天特卖」团队的创业合伙人!\" /><published>2021-11-11T19:59:43+00:00</published><updated>2021-11-11T19:59:43+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2021/11/11/captain-tttm</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2021/11/11/captain-tttm/\"><p><img src="/img/src/2021-11-11-captain-tttm-1.jpg" alt="imagee" /></p>\n\n<h3 id="天天特卖团队理念">天天特卖团队理念</h3>\n\n<h4 id="特卖合伙人">特卖合伙人</h4>\n\n<p>以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。特卖核心管理团队每 Q 会进行一次班子建设通晒。</p>\n\n<p><img src="/img/src/2021-11-11-captain-tttm-9.jpg" alt="imagee" /></p>\n\n<h4 id="如何理解协作">如何理解协作?</h4>\n\n<p>从长时间线来看,我们是为了不断积累信用,像一张信用卡一样,不断获得别人愿意支持我们的更大额度。不要事情结果还可以,而我们却没有积累到信用。互联网本质也是现代工业。而现代工业,一是社会分工,二是社会协作。想取得现代工业项目的结果,就要有更大的人才包容度、环境包容度。工作的结果就是在妥协与博弈中取得的,这是和光同尘的本质,也是现代工业复杂系统拿到结果的本质。只有这样我们才能让越来越多的人追随我们一起 do something,这种追随不一定只有上下级才是,而是愿意并且相信和我们能到达更远的地方。这背后的信用,要我们一步一个脚印地去积累,对他人给予的信任要保持敬畏、如履薄冰、懂得感恩。对每一段阶段性或长或短结束的合作,都要表达感谢。</p>\n\n<p><img src="/img/src/2021-11-11-captain-tttm-8.jpg" alt="imagee" /></p>\n\n<h4 id="如何看待同学的优势及短板">如何看待同学的优势及短板?</h4>\n\n<ul>\n <li>优势:讲优势有两个可能的目的,要么组织会在未来任务分配上重点考虑发挥该同学优势的事情,要告诉 TA,要激励 TA,是 TA 前行的自信来源之一。要么是对于同学也把握不准的特点,我们明确告诉 TA 这是你被我欣赏的优点。</li>\n <li>短板:什么是要讲的短板?未来一段时间,最期待你补足提升的。一旦这方面显著进步,就会向上迈进很大一步,甚至可以突破自己当下成长的瓶颈。要花多少篇幅讲?要比优势,有更大篇幅去讲。讲完就结束了么?对这个短板,一定要表达态度,也一定要对是否有方法、什么方法来补足短板要和同学沟通。</li>\n <li>无论是优势,还是短板,要说到点儿上,不要说片儿汤话。要让同学们能够引起思考、启发的。</li>\n</ul>\n\n<p><img src="/img/src/2021-11-11-captain-tttm-10.jpg" alt="imagee" /></p>\n\n<h3 id="天天特卖期待你的加入">天天特卖期待你的加入!</h3>\n\n<p>新天天特卖缘起于「手淘下沉市场战役)」,于 2021 年初上线,以「极致性价比货源、裸价直降、全网比价、买贵必赔」打造手淘极致价格敏感人群的购物阵地。目前天天特卖团队有行业运营、用户运营、数据策略、整合营销、直播运营、内容运营等岗位,有兴趣的同学可以钉钉随时找我,期待你的加入!</p>\n\n<h4 id="欢迎添加我的微信sinosuperman-推荐自荐--">欢迎添加我的微信:sinosuperman 推荐、自荐 ^ ^</h4>\n\n<p><img src="/img/src/2021-11-11-captain-tttm-11.jpg" alt="imagee" />\n<img src="/img/src/2021-11-11-captain-tttm-2.jpg" alt="imagee" />\n<img src="/img/src/2021-11-11-captain-tttm-3.jpg" alt="imagee" />\n<img src="/img/src/2021-11-11-captain-tttm-4.jpg" alt="imagee" />\n<img src="/img/src/2021-11-11-captain-tttm-5.jpg" alt="imagee" />\n<img src="/img/src/2021-11-11-captain-tttm-6.jpg" alt="imagee" />\n<img src="/img/src/2021-11-11-captain-tttm-7.jpg" alt="imagee" /></p></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"thinking\" /><category term=\"思考\" /><summary type=\"html\">阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!</summary></entry><entry><title type=\"html\">麦克船长的阿里一年香(入职阿里一周年)</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2021/06/04/captain-alibaba-1st-anniversary/\" rel=\"alternate\" type=\"text/html\" title=\"麦克船长的阿里一年香(入职阿里一周年)\" /><published>2021-06-04T15:42:43+00:00</published><updated>2021-06-04T15:42:43+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2021/06/04/captain-alibaba-1st-anniversary</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2021/06/04/captain-alibaba-1st-anniversary/\"><p>To 钟超</p>\n\n<p>1 周年快乐!很开心我们有这样一段共事的机会,虽开始时有些许波折,但随着进一步相处,我们很快能做到彼此欣赏、英雄相惜、默契配合,也特别感谢你对我的信任和支持,这是一切共事的基础。你强大的自驱力、脑力、对新事物的理解学习能力,都是最近几手新人里比较突出的。特别钦佩于你的执着和初性,对一件事认定后,迸发出的强大战斗力和决心。今天特卖这个新业务需要扎下根基,还真的需要一些舍我其谁的胆魄和更为犀利的突破,我也相信「新特卖」能成为你在阿里又一代表作,我希望我们的团队能为之骄傲和自豪,我们能不负公司所托,真正在下沉市场这场硬仗上有所建树,井取得令我们自己感到骄傲的突破,一起加油。</p>\n\n<p>From 麦克船长的主管</p></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"thinking\" /><category term=\"思考\" /><summary type=\"html\">本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。</summary></entry><entry><title type=\"html\">担任淘宝产品总负责人的双十一,是怎样的体验?</title><link href=\"https://www.mikecaptain.com/pages/Poechant/2020/11/11/captain-double-eleven/\" rel=\"alternate\" type=\"text/html\" title=\"担任淘宝产品总负责人的双十一,是怎样的体验?\" /><published>2020-11-11T15:59:43+00:00</published><updated>2020-11-11T15:59:43+00:00</updated><id>https://www.mikecaptain.com/pages/Poechant/2020/11/11/captain-double-eleven</id><content type=\"html\" xml:base=\"https://www.mikecaptain.com/pages/Poechant/2020/11/11/captain-double-eleven/\"><p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\n\n<p><img src="/img/src/2020-11-11-captain-double-eleven-1.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-2.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-3.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-4.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-5.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-6.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-7.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-8.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-9.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-10.jpg" alt="image" />\n<img src="/img/src/2020-11-11-captain-double-eleven-11.jpg" alt="image" /></p></content><author><name>Poechant</name><email>zhongchao.ustc@gmail.com</email></author><category term=\"thinking\" /><category term=\"思考\" /><summary type=\"html\">本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</summary></entry></feed>","<!-- <pre width=\"100%\">\nsite: {{ site | jsonify | escape }}\npage: {{ page | jsonify | escape }}\nlayout: {{ layout | jsonify | escape }}\ncontent: {{ content | jsonify | escape }}\npaginator: {{ paginator | jsonify | escape }}\n</pre> -->\n\n<!-- Posts -->\n<ul id=\"posts\">\n\n\t{% for post in paginator.posts %}\n\n\t <li class=\"post\">\n\t \t<h2><a href=\"{% if site.baseurl == \"/\" %}{{ post.url }}{% else %}{{ post.url | prepend: site.baseurl }}{% endif %}\">{%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}</a></h2>\n <time datetime=\"{{ post.date | date_to_xmlschema }}\" class=\"by-line\"> <i>{{ post.date | date_to_string }}</i> </time>\n\t \t<p>{{ post.excerpt | strip_html | truncatewords:50 }}</p>\n\t </li>\n\n {% endfor %}\n\n</ul>\n","<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n{% if page.xsl %}<?xml-stylesheet type=\"text/xsl\" href=\"{{ \"/sitemap.xsl\" | absolute_url }}\"?>\n{% endif %}<urlset xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\" xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n{% assign collections = site.collections | where_exp:'collection','collection.output != false' %}{% for collection in collections %}{% assign docs = collection.docs | where_exp:'doc','doc.sitemap != false' %}{% for doc in docs %}<url>\n<loc>{{ doc.url | replace:'/index.html','/' | absolute_url | xml_escape }}</loc>\n{% if doc.last_modified_at or doc.date %}<lastmod>{{ doc.last_modified_at | default: doc.date | date_to_xmlschema }}</lastmod>\n{% endif %}</url>\n{% endfor %}{% endfor %}{% assign pages = site.html_pages | where_exp:'doc','doc.sitemap != false' | where_exp:'doc','doc.url != \"/404.html\"' %}{% for page in pages %}<url>\n<loc>{{ page.url | replace:'/index.html','/' | absolute_url | xml_escape }}</loc>\n{% if page.last_modified_at %}<lastmod>{{ page.last_modified_at | date_to_xmlschema }}</lastmod>\n{% endif %}</url>\n{% endfor %}{% assign static_files = page.static_files | where_exp:'page','page.sitemap != false' | where_exp:'page','page.name != \"404.html\"' %}{% for file in static_files %}<url>\n<loc>{{ file.path | replace:'/index.html','/' | absolute_url | xml_escape }}</loc>\n<lastmod>{{ file.modified_time | date_to_xmlschema }}</lastmod>\n</url>\n{% endfor %}</urlset>\n","Sitemap: {{ \"sitemap.xml\" | absolute_url }}\n"],"time":"2023-01-03 19:14:34 +0000","tags":{"直播技术":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</h2>\t\t\n\t<time datetime=\"2012-08-04T17:58:17+00:00\" class=\"by-line\">04 Aug 2012, 广州 | 作者 麦克船长 | 总计 5236 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一rtmfpserver-线程的启动和等待\" id=\"markdown-toc-一rtmfpserver-线程的启动和等待\">一、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的启动和等待</a> <ul>\n <li><a href=\"#1pocothread\" id=\"markdown-toc-1pocothread\">1、<code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code></a></li>\n <li><a href=\"#2封装一个可运行线程的类\" id=\"markdown-toc-2封装一个可运行线程的类\">2、封装一个可运行线程的类</a></li>\n <li><a href=\"#3启动-rtmfpserver-线程\" id=\"markdown-toc-3启动-rtmfpserver-线程\">3、启动 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</a></li>\n <li><a href=\"#4rtmfpserver-线程等待\" id=\"markdown-toc-4rtmfpserver-线程等待\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程等待</a></li>\n </ul>\n </li>\n <li><a href=\"#二rtmfpmanager-对-rtmfpserver-的影响\" id=\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\">二、<code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的影响</a></li>\n</ul>\n\n<h3 id=\"一rtmfpserver-线程的启动和等待\">一、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的启动和等待</h3>\n\n<h4 id=\"1pocothread\">1、<code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code></h4>\n\n<p>Cumulus 大量使用了 <code class=\"language-plaintext highlighter-rouge\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">PoechantRunnable</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Runnable</span> <span class=\"p\">{</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"c1\">// your codes</span>\n <span class=\"p\">}</span>\n<span class=\"p\">};</span>\n \n<span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">PoechantRunnable</span> <span class=\"n\">runnable</span><span class=\"p\">;</span> <span class=\"c1\">// Image that it's a gift</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Thread</span> <span class=\"kr\">thread</span><span class=\"p\">;</span> <span class=\"c1\">// And… thread is just like your girl</span>\n <span class=\"kr\">thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">runnable</span><span class=\"p\">);</span> <span class=\"c1\">// Okay, give your sweet babe the gift :)</span>\n <span class=\"kr\">thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2封装一个可运行线程的类\">2、封装一个可运行线程的类</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中实现了一个 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 类,该类继承了 <code class=\"language-plaintext highlighter-rouge\">Runnable</code>,就是上面那个 <code class=\"language-plaintext highlighter-rouge\">gift</code> 喽。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">StartableProcess</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Runnable</span><span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">StartableProcess</span><span class=\"p\">(</span><span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">startable</span><span class=\"p\">);</span>\n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">run</span><span class=\"p\">();</span>\n <span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">_startable</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>可以看到其中有 <code class=\"language-plaintext highlighter-rouge\">Startable& _startable</code> 引用成员,它并没有继承 <code class=\"language-plaintext highlighter-rouge\">Runnable</code>,而是封装了 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 和 <code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Thread</span> <span class=\"kr\">_thread</span><span class=\"p\">;</span>\n<span class=\"n\">StartableProcess</span> <span class=\"n\">_process</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>这里 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 封装了一个 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 成员,与 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\n\n<h4 id=\"3启动-rtmfpserver-线程\">3、启动 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</h4>\n<p>我们可以看到在 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的构造函数中初始化了 <code class=\"language-plaintext highlighter-rouge\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\"language-plaintext highlighter-rouge\">(Flag Field)_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">true</code>,因为它还没有调用启动函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_name</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">),</span>\n <span class=\"kr\">_thread</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">),</span>\n <span class=\"n\">_stop</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_haveToJoin</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">_process</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>初始化 <code class=\"language-plaintext highlighter-rouge\">_process</code> 时,调用 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 构造函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">StartableProcess</span><span class=\"o\">::</span><span class=\"n\">StartableProcess</span><span class=\"p\">(</span><span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">startable</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_startable</span><span class=\"p\">(</span><span class=\"n\">startable</span><span class=\"p\">){</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>传入 <code class=\"language-plaintext highlighter-rouge\">_startable</code> 的引用。在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的,然后通过调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 来启动,启动后会响应到 <code class=\"language-plaintext highlighter-rouge\">run()</code>。下面我们以 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程为例。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 类是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">RTMFPServer</span>\n <span class=\"o\">:</span> <span class=\"k\">private</span> <span class=\"n\">Gateway</span><span class=\"p\">,</span>\n <span class=\"k\">protected</span> <span class=\"n\">Handler</span><span class=\"p\">,</span>\n <span class=\"k\">private</span> <span class=\"n\">Startable</span><span class=\"p\">,</span>\n <span class=\"k\">private</span> <span class=\"n\">SocketHandler</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的构造函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">RTMFPServer</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">cores</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer\"</span><span class=\"p\">),</span>\n <span class=\"n\">_sendingEngine</span><span class=\"p\">(</span><span class=\"n\">cores</span><span class=\"p\">),</span>\n <span class=\"n\">_receivingEngine</span><span class=\"p\">(</span><span class=\"n\">cores</span><span class=\"p\">),</span>\n <span class=\"n\">_pCirrus</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_handshake</span><span class=\"p\">(</span><span class=\"n\">_receivingEngine</span><span class=\"p\">,</span>\n <span class=\"n\">_sendingEngine</span><span class=\"p\">,</span>\n <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">),</span>\n <span class=\"n\">_sessions</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\n\n<table>\n <thead>\n <tr>\n <th>所在线程</th>\n <th>调用者</th>\n <th>函数</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>主线程</td>\n <td>main(…)</td>\n <td> </td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::start()</td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer对象</td>\n <td>Startable::start()</td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer从Startable继承来的Thread成员</td>\n <td>Thread::start(…)</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\n <td>StartableProcess::run()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::prerun()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>Startable::prerun()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::run()</td>\n </tr>\n </tbody>\n</table>\n\n<h4 id=\"4rtmfpserver-线程等待\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程等待</h4>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">terminate</span><span class=\"p\">)</span>\n <span class=\"n\">handle</span><span class=\"p\">(</span><span class=\"n\">terminate</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">handle(…)</code> 函数很简单,如下只进行了 <code class=\"language-plaintext highlighter-rouge\">sleep(...)</code> 和 <code class=\"language-plaintext highlighter-rouge\">giveHandle()</code> 两个操作。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">handle</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">){</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">sleep</span><span class=\"p\">()</span> <span class=\"o\">!=</span> <span class=\"n\">STOP</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">giveHandle</span><span class=\"p\">();</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span>\n <span class=\"n\">terminate</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">sleep(…)</code> 是 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 是从 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 继承而来的,声明如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">WakeUpType</span> <span class=\"nf\">sleep</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>定义如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">WakeUpType</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"n\">WakeUpType</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">WAKEUP</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">tryWait</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">))</span>\n <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">TIMEOUT</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>在运行状态下,<code class=\"language-plaintext highlighter-rouge\">_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">false</code>,而默认参数 <code class=\"language-plaintext highlighter-rouge\">timeout</code> 为 0,所以会调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>这个 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent</code> 成员是一个 <code class=\"language-plaintext highlighter-rouge\">Poco::Event</code> 对象,<code class=\"language-plaintext highlighter-rouge\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\"language-plaintext highlighter-rouge\">Poco::Event::wait()</code> 后,会一直等待 <code class=\"language-plaintext highlighter-rouge\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\"language-plaintext highlighter-rouge\">wait</code> 的状态。在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中 <code class=\"language-plaintext highlighter-rouge\">set</code> 的动作是由:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPServer::requestHandle()</code></li>\n <li><code class=\"language-plaintext highlighter-rouge\">PoolThread::push(Poco::AutoPtr<RunnableType>& pRunnable)</code></li>\n</ul>\n\n<p>执行的。</p>\n\n<h3 id=\"二rtmfpmanager-对-rtmfpserver-的影响\">二、<code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的影响</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 与 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 同样,继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">RTMFPManager</span> <span class=\"o\">:</span> <span class=\"k\">private</span> <span class=\"n\">Task</span><span class=\"p\">,</span> <span class=\"k\">private</span> <span class=\"n\">Startable</span>\n</code></pre></div></div>\n\n<p>在构造函数中将 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\"language-plaintext highlighter-rouge\">_server</code> 引用成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">RTMFPManager</span><span class=\"p\">(</span><span class=\"n\">RTMFPServer</span><span class=\"o\">&</span> <span class=\"n\">server</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_server</span><span class=\"p\">(</span><span class=\"n\">server</span><span class=\"p\">),</span>\n <span class=\"n\">Task</span><span class=\"p\">(</span><span class=\"n\">server</span><span class=\"p\">),</span>\n <span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPManager\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">start</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n\n<span class=\"cm\">/* ...... */</span>\n\n<span class=\"n\">RTMFPServer</span><span class=\"o\">&</span> <span class=\"n\">_server</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的构造函数中调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 成员函数,是从 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的线程。然后响应到 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager::run()</code> 函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">setPriority</span><span class=\"p\">(</span><span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">PRIO_LOW</span><span class=\"p\">);</span>\n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">2000</span><span class=\"p\">)</span><span class=\"o\">!=</span><span class=\"n\">STOP</span><span class=\"p\">)</span>\n <span class=\"n\">waitHandle</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这里要强调的是,这里的 <code class=\"language-plaintext highlighter-rouge\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\n\n<p>在这里我们可以看到 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的 <code class=\"language-plaintext highlighter-rouge\">handle(…)</code> 中的 <code class=\"language-plaintext highlighter-rouge\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的主循环的等待时间的。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">WakeUpType</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"n\">WakeUpType</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">WAKEUP</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">tryWait</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">))</span>\n <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">TIMEOUT</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>你可以自行修改 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 中 <code class=\"language-plaintext highlighter-rouge\">sleep(...)</code> 的参数,这样就会调用 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\"language-plaintext highlighter-rouge\">timeout</code>)来进行睡眠。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\"language-plaintext highlighter-rouge\">handle</code> 成员函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">handle</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_server</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这里就会调用到 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 源码时知道 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::manage()</code> 函数并不是在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程内运行的,而是 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">manage</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\"language-plaintext highlighter-rouge\">Session</code>。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</title>\n \t<meta name=\"description\" content=\"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</h2>\t\t\n\t<time datetime=\"2012-07-23T03:07:43+00:00\" class=\"by-line\">23 Jul 2012, 广州 | 作者 麦克船长 | 总计 3111 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#1客户端发布publishing-on-client-side\" id=\"markdown-toc-1客户端发布publishing-on-client-side\">1、客户端发布(Publishing on client side)</a></li>\n <li><a href=\"#2服务器端server-side\" id=\"markdown-toc-2服务器端server-side\">2、服务器端(Server-side)</a></li>\n <li><a href=\"#3客户端订阅subscribing-on-client-side\" id=\"markdown-toc-3客户端订阅subscribing-on-client-side\">3、客户端订阅(Subscribing on client side)</a></li>\n <li><a href=\"#4reference\" id=\"markdown-toc-4reference\">4、Reference</a></li>\n</ul>\n\n<p>整个流程概括如下:</p>\n\n<p>Flash 客户端通过 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 与 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 建立连接,然后通过 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\n\n<h3 id=\"1客户端发布publishing-on-client-side\">1、客户端发布(Publishing on client side)</h3>\n\n<p>通过 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\"/2012/04/10/openrtmfp-cumulus-1/\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\"language-plaintext highlighter-rouge\">nc</code> 是一个 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 对象。</p>\n\n<div class=\"language-actionscript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">nc</span><span class=\"p\">.</span><span class=\"nx\">connect</span><span class=\"p\">(</span><span class=\"s2\">\"rtmfp://localhost:1935\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\"language-plaintext highlighter-rouge\">ns1</code> 是一个 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 对象。</p>\n\n<div class=\"language-actionscript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">ns1</span><span class=\"p\">.</span><span class=\"nx\">publish</span><span class=\"p\">(</span><span class=\"s2\">\"poechant_media_flow\"</span><span class=\"p\">,</span> <span class=\"s2\">\"live\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\n\n<h3 id=\"2服务器端server-side\">2、服务器端(Server-side)</h3>\n\n<p>Cumulus 通过 <code class=\"language-plaintext highlighter-rouge\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">receive</span><span class=\"p\">(</span><span class=\"n\">RTMFPReceiving</span><span class=\"o\">&</span> <span class=\"n\">rtmfpReceiving</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\n\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\"language-plaintext highlighter-rouge\">Handshake</code> 类的 <code class=\"language-plaintext highlighter-rouge\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">ServerSession</span><span class=\"o\">::</span><span class=\"n\">packetHandler</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">packet</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Flow</span><span class=\"o\">::</span><span class=\"n\">fragmentSortedHandler</span><span class=\"p\">(</span><span class=\"n\">UInt64</span> <span class=\"n\">stage</span><span class=\"p\">,</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">fragment</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">flags</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">switch</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AMF_WITH_HANDLER</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AMF</span><span class=\"p\">:</span>\n <span class=\"n\">messageHandler</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">amf</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AUDIO</span><span class=\"p\">:</span>\n <span class=\"n\">audioHandler</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">VIDEO</span><span class=\"p\">:</span>\n <span class=\"n\">videoHandler</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"nl\">default:</span>\n <span class=\"n\">rawHandler</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>接下来在 <code class=\"language-plaintext highlighter-rouge\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span> <span class=\"o\">!=</span> <span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushAudioPacket</span><span class=\"p\">(</span><span class=\"n\">time</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n \n<span class=\"k\">for</span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span><span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span><span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushVideoPacket</span><span class=\"p\">(</span><span class=\"n\">time</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n \n<span class=\"k\">for</span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span><span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span><span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushDataPacket</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中的 <code class=\"language-plaintext highlighter-rouge\">_listeners</code> 就是该 <code class=\"language-plaintext highlighter-rouge\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Listener</span><span class=\"o\">&</span> <span class=\"n\">addListener</span><span class=\"p\">(</span>\n <span class=\"n\">Peer</span><span class=\"o\">&</span> <span class=\"n\">peer</span><span class=\"p\">,</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">id</span><span class=\"p\">,</span>\n <span class=\"n\">FlowWriter</span><span class=\"o\">&</span> <span class=\"n\">writer</span><span class=\"p\">,</span>\n <span class=\"kt\">bool</span> <span class=\"n\">unbuffered</span><span class=\"p\">);</span>\n \n<span class=\"kt\">void</span> <span class=\"nf\">removeListener</span><span class=\"p\">(</span>\n <span class=\"n\">Peer</span><span class=\"o\">&</span> <span class=\"n\">peer</span><span class=\"p\">,</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">id</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这两个函数来实现的。</p>\n\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\n\n<h3 id=\"3客户端订阅subscribing-on-client-side\">3、客户端订阅(Subscribing on client side)</h3>\n\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>ns2.play(\"poechant_media_flow\");\n</code></pre></div></div>\n\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\"language-plaintext highlighter-rouge\">NetStream::send(…)</code> 的,用于发送 <code class=\"language-plaintext highlighter-rouge\">Data</code>,<code class=\"language-plaintext highlighter-rouge\">Audio</code> 和 <code class=\"language-plaintext highlighter-rouge\">Video</code> 的程序可以参考该例修改。</p>\n\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 初始化的时候,传入 <code class=\"language-plaintext highlighter-rouge\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\"language-plaintext highlighter-rouge\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\n\n<h3 id=\"4reference\">4、Reference</h3>\n\n<ul>\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\n</ul>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</h2>\t\t\n\t<time datetime=\"2012-06-25T02:56:26+00:00\" class=\"by-line\">25 Jun 2012, 广州 | 作者 麦克船长 | 总计 2111 字</time>\n\t<div class=\"content\">\n\t\t<p><code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中的线程都是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code>,在其中封装 <code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\"language-plaintext highlighter-rouge\">Startable</code> 中的 <code class=\"language-plaintext highlighter-rouge\">start</code> 函数如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">)</span> <span class=\"c1\">// if running</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n \n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span>\n <span class=\"s\">\"Try to start up a new thread inherited from Startable\"</span><span class=\"p\">);</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">_process</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> \n <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span>\n <span class=\"s\">\"Impossible to start the thread : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样一个类继承 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code>,然后调用到该类自己的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\"language-plaintext highlighter-rouge\">SocketManager</code> 为例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">SocketManager</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"err\">…</span> \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"err\">…</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>我们要看看这个 <code class=\"language-plaintext highlighter-rouge\">running()</code> 是怎么回事,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">bool</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">running</span><span class=\"p\">()</span> <span class=\"k\">const</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>很简单,就是通过 <code class=\"language-plaintext highlighter-rouge\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 是什么时候被设置为 <code class=\"language-plaintext highlighter-rouge\">false</code> 的呢?就是上面的 <code class=\"language-plaintext highlighter-rouge\">start()</code>,这里存在的一个问题就是先 <code class=\"language-plaintext highlighter-rouge\">start</code> 线程,再设置 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_thread.start(_process);\n_stop=false;\n</code></pre></div></div>\n\n<p>而 <code class=\"language-plaintext highlighter-rouge\">start()</code> 之后 <code class=\"language-plaintext highlighter-rouge\">run()</code> 的时候就开始通过 <code class=\"language-plaintext highlighter-rouge\">running()</code> 来判断 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 值了。所以你会在使用 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\" alt=\"image\" /></p>\n\n<p>它们是:</p>\n\n<ul>\n <li>主线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 线程</li>\n</ul>\n\n<p>而异常情况可能是 <code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 没有启动,甚至 <code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 都没有启动。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\" alt=\"image\" /></p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 都没有启动的情况 T.T</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\" alt=\"image\" /></p>\n\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\n\n<p>解决办法就是将 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 值为 <code class=\"language-plaintext highlighter-rouge\">true</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">)</span> <span class=\"c1\">// if running</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n \n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span>\n <span class=\"s\">\"Try to start up a new thread inherited from Startable\"</span><span class=\"p\">);</span>\n <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">_process</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> \n <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span> \n <span class=\"c1\">// June 25th, 2012, Michael@YY</span>\n <span class=\"p\">}</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span>\n <span class=\"s\">\"Impossible to start the thread : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</h2>\t\t\n\t<time datetime=\"2012-06-07T15:34:18+00:00\" class=\"by-line\">07 Jun 2012, 广州 | 作者 麦克船长 | 总计 1538 字</time>\n\t<div class=\"content\">\n\t\t<p>OpenRTMFP/Cumulus 提供了 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>。</p>\n\n<p>一般来说,Thread A 会准备好要 <code class=\"language-plaintext highlighter-rouge\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\"language-plaintext highlighter-rouge\">push</code> 消息。</p>\n\n<p>但是 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\"language-plaintext highlighter-rouge\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\" alt=\"image\" /></p>\n\n<p>由于在 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\"language-plaintext highlighter-rouge\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\"language-plaintext highlighter-rouge\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\" alt=\"image\" /></p>\n\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">createAMFMessage</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">)</span>\n \n <span class=\"c1\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"p\">(</span><span class=\"n\">_closed</span> <span class=\"o\">||</span> <span class=\"n\">signature</span><span class=\"p\">.</span><span class=\"n\">empty</span><span class=\"p\">()</span> <span class=\"o\">||</span> <span class=\"n\">_band</span><span class=\"p\">.</span><span class=\"n\">failed</span><span class=\"p\">()))</span> <span class=\"p\">{</span>\n <span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">MessageBuffered</span><span class=\"p\">();</span>\n <span class=\"n\">MessageBuffered</span><span class=\"o\">&</span> <span class=\"n\">message</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"n\">writeResponseHeader</span><span class=\"p\">(</span><span class=\"n\">message</span><span class=\"p\">.</span><span class=\"n\">rawWriter</span><span class=\"p\">,</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">pMessage</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">MessageBuffered</span><span class=\"o\">&</span> <span class=\"n\">message</span><span class=\"p\">(</span><span class=\"n\">_MessageNull</span><span class=\"p\">);</span>\n <span class=\"n\">writeResponseHeader</span><span class=\"p\">(</span><span class=\"n\">message</span><span class=\"p\">.</span><span class=\"n\">rawWriter</span><span class=\"p\">,</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">NULL</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>然后再调用时最后再增加 <code class=\"language-plaintext highlighter-rouge\">push</code> 操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"kt\">void</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">pushAMFMessage</span><span class=\"p\">(</span><span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMessage</span> <span class=\"o\">!=</span> <span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_messages</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\" alt=\"image\" /></p>\n\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"kt\">void</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">pushAMFMessage</span><span class=\"p\">(</span><span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMessage</span> <span class=\"o\">!=</span> <span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Mutex</span><span class=\"o\">::</span><span class=\"n\">ScopedLock</span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">msgQueueMutex</span><span class=\"p\">);</span>\n <span class=\"n\">_messages</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样就基本解决了这个线程安全问题。</p>\n\n<p>另外,使用 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</h2>\t\t\n\t<time datetime=\"2012-04-24T03:31:10+00:00\" class=\"by-line\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 12668 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一流缓冲区\" id=\"markdown-toc-一流缓冲区\">一、流缓冲区</a> <ul>\n <li><a href=\"#1了解-stdstreambuf\" id=\"markdown-toc-1了解-stdstreambuf\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::streambuf</code></a> <ul>\n <li><a href=\"#11单步移动内置指针\" id=\"markdown-toc-11单步移动内置指针\">1.1、单步移动内置指针</a></li>\n <li><a href=\"#12获取-get-指针和-put-指针的位置\" id=\"markdown-toc-12获取-get-指针和-put-指针的位置\">1.2、获取 get 指针和 put 指针的位置</a></li>\n <li><a href=\"#13设置-get-和-put-指针可达区域的上下界\" id=\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\">1.3、设置 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针可达区域的上下界</a></li>\n </ul>\n </li>\n <li><a href=\"#2memorystreambuf\" id=\"markdown-toc-2memorystreambuf\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code></a> <ul>\n <li><a href=\"#21移动内置的-get-和-put-指针\" id=\"markdown-toc-21移动内置的-get-和-put-指针\">2.1、移动内置的 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针:</a></li>\n <li><a href=\"#22获取-get-和-put-指针当前位置\" id=\"markdown-toc-22获取-get-和-put-指针当前位置\">2.2、获取 get 和 put 指针当前位置:</a></li>\n <li><a href=\"#23获取缓冲区的起始位置和大小\" id=\"markdown-toc-23获取缓冲区的起始位置和大小\">2.3、获取缓冲区的起始位置和大小:</a></li>\n <li><a href=\"#24缓冲区的已写字节数\" id=\"markdown-toc-24缓冲区的已写字节数\">2.4、缓冲区的已写字节数</a></li>\n <li><a href=\"#25显式设定-put-和-get-指针位置\" id=\"markdown-toc-25显式设定-put-和-get-指针位置\">2.5、显式设定 <code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针位置</a></li>\n <li><a href=\"#26-修改缓冲区大小\" id=\"markdown-toc-26-修改缓冲区大小\">2.6 修改缓冲区大小</a></li>\n <li><a href=\"#27构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-27构造函数拷贝构造函数和析构函数\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#二io-流\" id=\"markdown-toc-二io-流\">二、IO 流</a> <ul>\n <li><a href=\"#1了解-stdios\" id=\"markdown-toc-1了解-stdios\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::ios</code></a></li>\n <li><a href=\"#2memoryios\" id=\"markdown-toc-2memoryios\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code></a> <ul>\n <li><a href=\"#21构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-21构造函数拷贝构造函数和析构函数\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\n <li><a href=\"#22得到-memorystreambuf-成员的地址\" id=\"markdown-toc-22得到-memorystreambuf-成员的地址\">2.2、得到 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的地址</a></li>\n <li><a href=\"#23当前位置\" id=\"markdown-toc-23当前位置\">2.3、当前位置</a></li>\n <li><a href=\"#24封装-memorystreambuf-成员的一些函数\" id=\"markdown-toc-24封装-memorystreambuf-成员的一些函数\">2.4、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的一些函数</a></li>\n <li><a href=\"#25-缓冲区可读数据的字节数\" id=\"markdown-toc-25-缓冲区可读数据的字节数\">2.5 缓冲区可读数据的字节数</a></li>\n </ul>\n </li>\n <li><a href=\"#3输入流\" id=\"markdown-toc-3输入流\">3、输入流</a></li>\n <li><a href=\"#4输出流\" id=\"markdown-toc-4输出流\">4、输出流</a> <ul>\n <li><a href=\"#41-构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-41-构造函数拷贝构造函数和析构函数\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\n <li><a href=\"#42-读取和设定已写字节数\" id=\"markdown-toc-42-读取和设定已写字节数\">4.2 读取和设定已写字节数</a></li>\n <li><a href=\"#43-当前位置\" id=\"markdown-toc-43-当前位置\">4.3 当前位置</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#三局部内存片\" id=\"markdown-toc-三局部内存片\">三、局部内存片</a> <ul>\n <li><a href=\"#1构造函数\" id=\"markdown-toc-1构造函数\">1、构造函数</a></li>\n <li><a href=\"#2析构函数\" id=\"markdown-toc-2析构函数\">2、析构函数</a></li>\n <li><a href=\"#3缓冲区切割\" id=\"markdown-toc-3缓冲区切割\">3、缓冲区切割</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\n\n<h3 id=\"一流缓冲区\">一、流缓冲区</h3>\n\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\n\n<h4 id=\"1了解-stdstreambuf\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::streambuf</code></h4>\n\n<p>首先要了解 <code class=\"language-plaintext highlighter-rouge\">streambuf</code> 内置了一个 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针和一个 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针。<code class=\"language-plaintext highlighter-rouge\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\"language-plaintext highlighter-rouge\">g</code> 和 <code class=\"language-plaintext highlighter-rouge\">p</code> 就分别表示 get pointer 和 put pointer。</p>\n\n<h5 id=\"11单步移动内置指针\">1.1、单步移动内置指针</h5>\n\n<p>Increase get pointer: Advances the get pointer by <code class=\"language-plaintext highlighter-rouge\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">gbump</span> <span class=\"p\">(</span> <span class=\"kt\">int</span> <span class=\"n\">n</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>Increase put pointer: Advances the put pointer by <code class=\"language-plaintext highlighter-rouge\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">pbump</span> <span class=\"p\">(</span> <span class=\"kt\">int</span> <span class=\"n\">n</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<h5 id=\"12获取-get-指针和-put-指针的位置\">1.2、获取 get 指针和 put 指针的位置</h5>\n\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">char</span> <span class=\"o\">*</span> <span class=\"n\">gptr</span> <span class=\"p\">(</span> <span class=\"p\">)</span> <span class=\"k\">const</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">char</span> <span class=\"o\">*</span> <span class=\"n\">pptr</span> <span class=\"p\">(</span> <span class=\"p\">)</span> <span class=\"k\">const</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h5 id=\"13设置-get-和-put-指针可达区域的上下界\">1.3、设置 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针可达区域的上下界</h5>\n\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">setg</span> <span class=\"p\">(</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gbeg</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gnext</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gend</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\n</ul>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">setp</span> <span class=\"p\">(</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pbeg</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pend</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\n</ul>\n\n<h4 id=\"2memorystreambuf\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code></h4>\n\n<p>类定义:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryStreamBuf</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">streambuf</span> <span class=\"p\">{</span>\n <span class=\"k\">friend</span> <span class=\"k\">class</span> <span class=\"nc\">ScopedMemoryClip</span><span class=\"p\">;</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">written</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gCurrent</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pCurrent</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n \n<span class=\"nl\">private:</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">overflow</span><span class=\"p\">(</span><span class=\"n\">int_type</span> <span class=\"n\">c</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">underflow</span><span class=\"p\">();</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">sync</span><span class=\"p\">();</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_written</span><span class=\"p\">;</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">();</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"k\">operator</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span><span class=\"p\">);</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 是 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\n\n<h5 id=\"21移动内置的-get-和-put-指针\">2.1、移动内置的 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针:</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针都移动:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">gbump</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22获取-get-和-put-指针当前位置\">2.2、获取 get 和 put 指针当前位置:</h5>\n\n<p>封装 <code class=\"language-plaintext highlighter-rouge\">streambuf</code> 的 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 和 <code class=\"language-plaintext highlighter-rouge\">pptr</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">gCurrent</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">gptr</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">pptr</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23获取缓冲区的起始位置和大小\">2.3、获取缓冲区的起始位置和大小:</h5>\n\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"24缓冲区的已写字节数\">2.4、缓冲区的已写字节数</h5>\n\n<p>读取(其中也可能发生设置操作):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"kt\">int</span> <span class=\"n\">written</span> <span class=\"o\">=</span> <span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"c1\">// 已写字节数</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">written</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">written</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">written</span> <span class=\"o\">></span> <span class=\"n\">_written</span><span class=\"p\">)</span> <span class=\"c1\">// 保存已写字节数</span>\n <span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"p\">)</span><span class=\"n\">written</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">_written</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设置:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_written</span><span class=\"o\">=</span><span class=\"n\">size</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"25显式设定-put-和-get-指针位置\">2.5、显式设定 <code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针位置</h5>\n\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"c1\">// 保存已写字节数</span>\n <span class=\"n\">written</span><span class=\"p\">();</span> <span class=\"c1\">// Save nb char written</span>\n \n <span class=\"c1\">// 移动 put 指针</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n <span class=\"n\">pbump</span><span class=\"p\">((</span><span class=\"kt\">int</span><span class=\"p\">)</span> <span class=\"n\">pos</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 移动 get 指针</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">pos</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"26-修改缓冲区大小\">2.6 修改缓冲区大小</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"c1\">// 大小标识</span>\n <span class=\"n\">_bufferSize</span> <span class=\"o\">=</span> <span class=\"n\">newSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// gptr 当前位置</span>\n <span class=\"kt\">int</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">gCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 gptr 可达范围和当前位置</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">pos</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span> \n <span class=\"c1\">// pptr 当前位置</span>\n <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 pptr 可达范围和当前位置</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"27构造函数拷贝构造函数和析构函数\">2.7、构造函数、拷贝构造函数和析构函数</h5>\n\n<p>构造函数会设定 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 和 <code class=\"language-plaintext highlighter-rouge\">gptr</code>,并初始化 <code class=\"language-plaintext highlighter-rouge\">pBuffer</code> 和 <code class=\"language-plaintext highlighter-rouge\">bufferSize</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> <span class=\"n\">_pBuffer</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">),</span><span class=\"n\">_bufferSize</span><span class=\"p\">(</span><span class=\"n\">bufferSize</span><span class=\"p\">),</span><span class=\"n\">_written</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span><span class=\"p\">,</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构函数会拷贝对方的 <code class=\"language-plaintext highlighter-rouge\">pBuffer</code>、<code class=\"language-plaintext highlighter-rouge\">bufferSizse</code>、<code class=\"language-plaintext highlighter-rouge\">_written</code>,并设定 <code class=\"language-plaintext highlighter-rouge\">gptr</code>、<code class=\"language-plaintext highlighter-rouge\">pptr</code>。注意设定 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 时,要分别调用 <code class=\"language-plaintext highlighter-rouge\">setp</code> 和 <code class=\"language-plaintext highlighter-rouge\">pbump</code>,因为 <code class=\"language-plaintext highlighter-rouge\">setp</code> 仅将 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span> <span class=\"n\">_pBuffer</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">),</span><span class=\"n\">_bufferSize</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">),</span><span class=\"n\">_written</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_written</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">gCurrent</span><span class=\"p\">(),</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">pbump</span><span class=\"p\">((</span><span class=\"kt\">int</span><span class=\"p\">)(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"n\">_pBuffer</span><span class=\"p\">));</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::~</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"二io-流\">二、IO 流</h3>\n\n<h4 id=\"1了解-stdios\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::ios</code></h4>\n\n<p>Initialize object [<code class=\"language-plaintext highlighter-rouge\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">init</span> <span class=\"p\">(</span> <span class=\"n\">streambuf</span><span class=\"o\">*</span> <span class=\"n\">sb</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>初始化后如下函数的返回值:</p>\n\n<table>\n <thead>\n <tr>\n <th>member function</th>\n <th>value</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>rdbuf()</td>\n <td>sb</td>\n </tr>\n <tr>\n <td>tie()</td>\n <td>0</td>\n </tr>\n <tr>\n <td>rdstate()</td>\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\n </tr>\n <tr>\n <td>exceptions()</td>\n <td>goodbit</td>\n </tr>\n <tr>\n <td>flags()</td>\n <td>skipws | dec</td>\n </tr>\n <tr>\n <td>width()</td>\n <td>0</td>\n </tr>\n <tr>\n <td>precision()</td>\n <td>6</td>\n </tr>\n <tr>\n <td>fill()</td>\n <td>‘ ’ (whitespace)</td>\n </tr>\n <tr>\n <td>getloc()</td>\n <td>a copy of locale()</td>\n </tr>\n </tbody>\n</table>\n\n<h4 id=\"2memoryios\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code></h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code>,且是 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 和 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\"language-plaintext highlighter-rouge\">std::ios</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryIOS</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"k\">virtual</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ios</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">MemoryIOS</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryIOS</span><span class=\"p\">();</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">*</span> <span class=\"n\">rdbuf</span><span class=\"p\">();</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">);</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"n\">MemoryStreamBuf</span> <span class=\"n\">_buf</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"21构造函数拷贝构造函数和析构函数\">2.1、构造函数、拷贝构造函数和析构函数</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span><span class=\"n\">_buf</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">poco_ios_init</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">poco_ios_init</code> 为 <code class=\"language-plaintext highlighter-rouge\">init</code> 的宏定义,用于初始化成员 <code class=\"language-plaintext highlighter-rouge\">_buf</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">MemoryIOS</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span><span class=\"n\">_buf</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_buf</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">poco_ios_init</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::~</span><span class=\"n\">MemoryIOS</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22得到-memorystreambuf-成员的地址\">2.2、得到 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的地址</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">*</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">rdbuf</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23当前位置\">2.3、当前位置</h5>\n\n<p>这是一个纯虚函数,由 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 和 <code class=\"language-plaintext highlighter-rouge\">MemoryOutpuStream</code> 继承时实现:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">virtual</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h5 id=\"24封装-memorystreambuf-成员的一些函数\">2.4、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的一些函数</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">resize</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">newSize</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code> 封装为 <code class=\"language-plaintext highlighter-rouge\">reset</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"o\">>=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"25-缓冲区可读数据的字节数\">2.5 缓冲区可读数据的字节数</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"kt\">int</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"p\">(</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">begin</span><span class=\"p\">());</span> <span class=\"c1\">// 缓冲区剩余可读数据字节数</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">result</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"p\">)</span><span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3输入流\">3、输入流</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryInputStream</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">MemoryIOS</span><span class=\"p\">,</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">istream</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"c1\">/// Creates a MemoryInputStream for the given memory area,</span>\n <span class=\"c1\">/// ready for reading.</span>\n <span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryInputStream</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">();</span>\n <span class=\"c1\">/// Destroys the MemoryInputStream.</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 以及 <code class=\"language-plaintext highlighter-rouge\">istream</code>。<code class=\"language-plaintext highlighter-rouge\">istream</code> 是 <code class=\"language-plaintext highlighter-rouge\">iostream</code> 中的 <code class=\"language-plaintext highlighter-rouge\">basic_istream</code> 别名(<code class=\"language-plaintext highlighter-rouge\">typedef</code>)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> \n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"k\">const_cast</span><span class=\"o\"><</span><span class=\"kt\">char</span><span class=\"o\">*></span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">),</span> <span class=\"n\">bufferSize</span><span class=\"p\">),</span> <span class=\"n\">istream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryInputStream</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">),</span> <span class=\"n\">istream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryInputStream</span><span class=\"o\">::~</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>唯一的一个成员函数是 <code class=\"language-plaintext highlighter-rouge\">current</code>,封装了 <code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 的 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的 <code class=\"language-plaintext highlighter-rouge\">gCurrent</code> 函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">gCurrent</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4输出流\">4、输出流</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryOutputStream</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">MemoryIOS</span><span class=\"p\">,</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ostream</span>\n <span class=\"c1\">/// An input stream for reading from a memory area.</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"c1\">/// Creates a MemoryOutputStream for the given memory area,</span>\n <span class=\"c1\">/// ready for writing.</span>\n <span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryOutputStream</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">();</span>\n <span class=\"c1\">/// Destroys the MemoryInputStream.</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">written</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"41-构造函数拷贝构造函数和析构函数\">4.1 构造函数、拷贝构造函数和析构函数</h5>\n\n<p>如下,不赘述了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> \n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">bufferSize</span><span class=\"p\">),</span> <span class=\"n\">ostream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n<span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryOutputStream</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">),</span> <span class=\"n\">ostream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryOutputStream</span><span class=\"o\">::~</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(){</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"42-读取和设定已写字节数\">4.2 读取和设定已写字节数</h5>\n\n<p>读取:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">written</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"43-当前位置\">4.3 当前位置</h5>\n\n<p>与 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 中的封装类似:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">pCurrent</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三局部内存片\">三、局部内存片</h3>\n\n<p>在第一部分的流缓冲区介绍 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 中有一个 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ScopedMemoryClip</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">ScopedMemoryClip</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">offset</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Int32</span> <span class=\"n\">offset</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_offset</span><span class=\"p\">;</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">_buffer</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"1构造函数\">1、构造函数</h4>\n\n<p>构造函数传入的参数对应的就是 <code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\"language-plaintext highlighter-rouge\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">offset</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_offset</span><span class=\"p\">(</span><span class=\"n\">offset</span><span class=\"p\">),</span> <span class=\"n\">_buffer</span><span class=\"p\">(</span><span class=\"n\">buffer</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_offset</span> <span class=\"o\">>=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">_offset</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span> <span class=\"o\">-</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_offset</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_offset</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">_offset</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2析构函数\">2、析构函数</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::~</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"o\">-</span><span class=\"p\">(</span><span class=\"n\">Int32</span><span class=\"p\">)</span><span class=\"n\">_offset</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3缓冲区切割\">3、缓冲区切割</h4>\n\n<p>可以看到构造函数和析构函数中都调用了 <code class=\"language-plaintext highlighter-rouge\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\n\n<ul>\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\n</ul>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::</span><span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">Int32</span> <span class=\"n\">offset</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"c1\">// 获取到 gptr</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gpos</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">gCurrent</span><span class=\"p\">();</span>\n \n <span class=\"c1\">// 偏移缓冲区地址,并修改缓冲区大小</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span> <span class=\"o\">-=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\n <span class=\"kt\">int</span> <span class=\"n\">ppos</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 gptr 可达区域和位置</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">gpos</span><span class=\"p\">,</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 设置 pptr 可达区域和位置</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">ppos</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\"><</span> <span class=\"n\">offset</span><span class=\"p\">)</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\n <span class=\"k\">else</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">-=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">></span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</h2>\t\t\n\t<time datetime=\"2012-04-24T02:04:55+00:00\" class=\"by-line\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 30820 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一amf-数据类型定义\" id=\"markdown-toc-一amf-数据类型定义\">一、AMF 数据类型定义</a> <ul>\n <li><a href=\"#1数据类型\" id=\"markdown-toc-1数据类型\">1、数据类型</a></li>\n <li><a href=\"#2undefined-type\" id=\"markdown-toc-2undefined-type\">2、<code class=\"language-plaintext highlighter-rouge\">undefined</code> Type</a></li>\n <li><a href=\"#3null-type\" id=\"markdown-toc-3null-type\">3、<code class=\"language-plaintext highlighter-rouge\">null</code> Type</a></li>\n <li><a href=\"#4false-type\" id=\"markdown-toc-4false-type\">4、<code class=\"language-plaintext highlighter-rouge\">false</code> type</a></li>\n <li><a href=\"#5true-type\" id=\"markdown-toc-5true-type\">5、<code class=\"language-plaintext highlighter-rouge\">true</code> type</a></li>\n <li><a href=\"#6integer-type\" id=\"markdown-toc-6integer-type\">6、<code class=\"language-plaintext highlighter-rouge\">integer</code> type</a></li>\n <li><a href=\"#7double-type\" id=\"markdown-toc-7double-type\">7、<code class=\"language-plaintext highlighter-rouge\">double</code> type</a></li>\n <li><a href=\"#8string-type\" id=\"markdown-toc-8string-type\">8、<code class=\"language-plaintext highlighter-rouge\">String</code> type</a></li>\n <li><a href=\"#9xmldocument-type\" id=\"markdown-toc-9xmldocument-type\">9、<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> type</a></li>\n <li><a href=\"#10date-type\" id=\"markdown-toc-10date-type\">10、<code class=\"language-plaintext highlighter-rouge\">Date</code> type</a></li>\n <li><a href=\"#11array-type\" id=\"markdown-toc-11array-type\">11、<code class=\"language-plaintext highlighter-rouge\">Array</code> type</a></li>\n <li><a href=\"#12object-type\" id=\"markdown-toc-12object-type\">12、<code class=\"language-plaintext highlighter-rouge\">Object</code> type</a></li>\n <li><a href=\"#13xml-type\" id=\"markdown-toc-13xml-type\">13、<code class=\"language-plaintext highlighter-rouge\">XML</code> type</a></li>\n <li><a href=\"#14bytearray-type\" id=\"markdown-toc-14bytearray-type\">14、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> type</a></li>\n <li><a href=\"#15amf3-的使用\" id=\"markdown-toc-15amf3-的使用\">15、AMF3 的使用</a> <ul>\n <li><a href=\"#151netconnection-and-amf-3\" id=\"markdown-toc-151netconnection-and-amf-3\">15.1、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> and AMF 3</a></li>\n <li><a href=\"#152netconnection-in-actionscript-30\" id=\"markdown-toc-152netconnection-in-actionscript-30\">15.2、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> in ActionScript 3.0</a></li>\n <li><a href=\"#153bytearray-idatainput-and-idataoutput\" id=\"markdown-toc-153bytearray-idatainput-and-idataoutput\">15.3、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code>, <code class=\"language-plaintext highlighter-rouge\">IDataInput</code> and <code class=\"language-plaintext highlighter-rouge\">IDataOutput</code></a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#二binaryreaderwriter\" id=\"markdown-toc-二binaryreaderwriter\">二、<code class=\"language-plaintext highlighter-rouge\">BinaryReader/Writer</code></a> <ul>\n <li><a href=\"#1amf3-数据格式基础\" id=\"markdown-toc-1amf3-数据格式基础\">1、AMF3 数据格式基础</a></li>\n <li><a href=\"#2序列化\" id=\"markdown-toc-2序列化\">2、序列化</a></li>\n <li><a href=\"#3反序列化\" id=\"markdown-toc-3反序列化\">3、反序列化</a></li>\n </ul>\n </li>\n <li><a href=\"#三packetreaderwriter\" id=\"markdown-toc-三packetreaderwriter\">三、<code class=\"language-plaintext highlighter-rouge\">PacketReader/Writer</code></a> <ul>\n <li><a href=\"#1packetreader\" id=\"markdown-toc-1packetreader\">1、PacketReader</a> <ul>\n <li><a href=\"#11封装-memoryinputstream\" id=\"markdown-toc-11封装-memoryinputstream\">1.1、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code></a></li>\n <li><a href=\"#12收缩缓冲区\" id=\"markdown-toc-12收缩缓冲区\">1.2、收缩缓冲区</a></li>\n <li><a href=\"#13构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-13构造函数拷贝构造函数和析构函数\">1.3、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n <li><a href=\"#2packetwriter\" id=\"markdown-toc-2packetwriter\">2、<code class=\"language-plaintext highlighter-rouge\">PacketWriter</code></a> <ul>\n <li><a href=\"#21封装memoryoutputstream\" id=\"markdown-toc-21封装memoryoutputstream\">2.1、封装<code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code></a></li>\n <li><a href=\"#22封装-binarywriter\" id=\"markdown-toc-22封装-binarywriter\">2.2、封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code></a></li>\n <li><a href=\"#23构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-23构造函数拷贝构造函数和析构函数\">2.3、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四amfreader\" id=\"markdown-toc-四amfreader\">四、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code></a> <ul>\n <li><a href=\"#1objectdef\" id=\"markdown-toc-1objectdef\">1、<code class=\"language-plaintext highlighter-rouge\">ObjectDef</code></a></li>\n <li><a href=\"#2amfreader-定义\" id=\"markdown-toc-2amfreader-定义\">2、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 定义</a> <ul>\n <li><a href=\"#21构造函数析构函数\" id=\"markdown-toc-21构造函数析构函数\">2.1、构造函数、析构函数</a></li>\n <li><a href=\"#22简单封装-packetreader-的一些函数\" id=\"markdown-toc-22简单封装-packetreader-的一些函数\">2.2、简单封装 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 的一些函数</a></li>\n <li><a href=\"#23设置-gptr-位置\" id=\"markdown-toc-23设置-gptr-位置\">2.3、设置 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 位置</a></li>\n <li><a href=\"#24判断类型\" id=\"markdown-toc-24判断类型\">2.4、判断类型</a></li>\n </ul>\n </li>\n <li><a href=\"#3解析-as3-null\" id=\"markdown-toc-3解析-as3-null\">3、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Null</code></a></li>\n <li><a href=\"#4解析-as3-number\" id=\"markdown-toc-4解析-as3-number\">4、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Number</code></a></li>\n <li><a href=\"#5解析-as3-integer\" id=\"markdown-toc-5解析-as3-integer\">5、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Integer</code></a></li>\n <li><a href=\"#6解析-as3-boolean\" id=\"markdown-toc-6解析-as3-boolean\">6、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Boolean</code></a></li>\n <li><a href=\"#7开始引用与结束引用\" id=\"markdown-toc-7开始引用与结束引用\">7、开始引用与结束引用</a></li>\n <li><a href=\"#8解析-as3-bytearray\" id=\"markdown-toc-8解析-as3-bytearray\">8、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code></a></li>\n <li><a href=\"#9解析-as3-date\" id=\"markdown-toc-9解析-as3-date\">9、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Date</code></a></li>\n <li><a href=\"#10解析-as3-dictionary\" id=\"markdown-toc-10解析-as3-dictionary\">10、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Dictionary</code></a></li>\n </ul>\n </li>\n</ul>\n\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。</p>\n\n<h3 id=\"一amf-数据类型定义\">一、AMF 数据类型定义</h3>\n\n<h4 id=\"1数据类型\">1、数据类型</h4>\n\n<p>各种数据类型的标示都在 AMF.h 中定义为宏</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cp\">#define AMF_NUMBER 0x00 // 浮点数\n#define AMF_BOOLEAN 0x01 // 布尔型\n#define AMF_STRING 0x02 // 字符串\n#define AMF_BEGIN_OBJECT 0x03 // 对象,开始\n#define AMF_NULL 0x05 // null\n#define AMF_UNDEFINED 0x06\n#define AMF_REFERENCE 0x07\n#define AMF_MIXED_ARRAY 0x08\n#define AMF_END_OBJECT 0x09 // 对象,结束\n#define AMF_BEGIN_TYPED_OBJECT 0x10\n#define AMF_STRICT_ARRAY 0x0A\n#define AMF_DATE 0x0B // 日期\n#define AMF_LONG_STRING 0x0C // 字符串\n#define AMF_UNSUPPORTED 0x0D\n</span> \n<span class=\"cp\">#define AMF_AVMPLUS_OBJECT 0x11\n#define AMF_END 0xFF\n</span> \n<span class=\"cp\">#define AMF3_UNDEFINED 0x00\n#define AMF3_NULL 0x01\n#define AMF3_FALSE 0x02\n#define AMF3_TRUE 0x03\n#define AMF3_INTEGER 0x04\n#define AMF3_NUMBER 0x05\n#define AMF3_STRING 0x06\n#define AMF3_DATE 0x08\n#define AMF3_ARRAY 0x09\n#define AMF3_OBJECT 0x0A\n#define AMF3_BYTEARRAY 0x0C\n#define AMF3_DICTIONARY 0x11\n</span></code></pre></div></div>\n\n<p>并定义了一个枚举类表示数据类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">AMF</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"k\">enum</span> <span class=\"n\">Type</span> <span class=\"p\">{</span>\n <span class=\"n\">Null</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">,</span>\n <span class=\"n\">Boolean</span><span class=\"p\">,</span>\n <span class=\"n\">Integer</span><span class=\"p\">,</span>\n <span class=\"n\">Number</span><span class=\"p\">,</span>\n <span class=\"n\">String</span><span class=\"p\">,</span>\n <span class=\"n\">Date</span><span class=\"p\">,</span>\n <span class=\"n\">Array</span><span class=\"p\">,</span>\n <span class=\"n\">Object</span><span class=\"p\">,</span>\n <span class=\"n\">ByteArray</span><span class=\"p\">,</span>\n <span class=\"n\">Dictionary</span><span class=\"p\">,</span>\n <span class=\"n\">RawObjectContent</span><span class=\"p\">,</span>\n <span class=\"n\">End</span>\n <span class=\"p\">};</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"2undefined-type\">2、<code class=\"language-plaintext highlighter-rouge\">undefined</code> Type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">undefined</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">undefined</code> 类型标记表示。此值不会编码任何其他信息。</p>\n\n<h4 id=\"3null-type\">3、<code class=\"language-plaintext highlighter-rouge\">null</code> Type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">null</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">null</code> 类型标记表示。此值不会编码任何其他信息。</p>\n\n<h4 id=\"4false-type\">4、<code class=\"language-plaintext highlighter-rouge\">false</code> type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">false</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">false</code> 类型标记表示,用于编码布尔值 <code class=\"language-plaintext highlighter-rouge\">false</code>。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\n\n<h4 id=\"5true-type\">5、<code class=\"language-plaintext highlighter-rouge\">true</code> type</h4>\n\n<p>true 类型由 true 类型标记表示,用于编码布尔值 true。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\n\n<h4 id=\"6integer-type\">6、<code class=\"language-plaintext highlighter-rouge\">integer</code> type</h4>\n\n<p>在 AMF 3 中,整数使用可变长度的无符号 29 位整数进行序列化。ActionScript 3.0 中的整数类型 - 有符号 <code class=\"language-plaintext highlighter-rouge\">int</code> 类型和无符号 <code class=\"language-plaintext highlighter-rouge\">uint</code> 类型 - 也使用 29 位在 AVM+中表示。如果无符号整数 (<code class=\"language-plaintext highlighter-rouge\">uint</code>) 的值大于等于 229 或者如果有符号整数 (<code class=\"language-plaintext highlighter-rouge\">int</code>) 的值大于等于 228,则它将被 AVM+ 表示为 <code class=\"language-plaintext highlighter-rouge\">double</code> 类型,并使用 AMF 3 double 类型进行序列化。</p>\n\n<h4 id=\"7double-type\">7、<code class=\"language-plaintext highlighter-rouge\">double</code> type</h4>\n\n<p>AMF 3 的 <code class=\"language-plaintext highlighter-rouge\">double</code> 类型与 AMF 0 的 <code class=\"language-plaintext highlighter-rouge\">Number</code> 类型编码方式相同。此类型用于编码 ActionScript <code class=\"language-plaintext highlighter-rouge\">Number</code> 或值大于等于 228 的 ActionScript <code class=\"language-plaintext highlighter-rouge\">int</code> 或值大于等于 229 的 ActionScript <code class=\"language-plaintext highlighter-rouge\">uint</code>。编码值始终是网络字节顺序中的 8 字节 IEEE-754 双精度浮点值 (低内存中的符号位)。</p>\n\n<h4 id=\"8string-type\">8、<code class=\"language-plaintext highlighter-rouge\">String</code> type</h4>\n\n<p>ActionScript String 值使用 AMF 3 中的单个 string 类型表示 - AMF 0 中的 <code class=\"language-plaintext highlighter-rouge\">string</code> 和 <code class=\"language-plaintext highlighter-rouge\">long string</code> 类型的概念不再使用。可以使用对隐式字符串引用表中的索引将字符串作为先前发生的字符串的引用发送。字符串使用 UTF-8 编码 - 但是头可以描述字符串文本或字符串引用。空字符串永远不会作为引用发送。</p>\n\n<h4 id=\"9xmldocument-type\">9、<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> type</h4>\n\n<p>ActionScript 3.0 引入了新的 XML 类型 (参见 3.13),但是旧版的 XMLDocument 类型在语言中被保留为 <code class=\"language-plaintext highlighter-rouge\">flash.xml.XMLDocument</code>。与 AMF 0 类似,<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> 的结构需要扁平化为字符串表示以进行序列化。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。XMLDocuments 可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> 实例的引用发送。</p>\n\n<h4 id=\"10date-type\">10、<code class=\"language-plaintext highlighter-rouge\">Date</code> type</h4>\n\n<p>在 AMF 3 中,ActionScript Date 简单地作为自 1970 年 1 月 1 日午夜 (UTC 时区) 以来的毫秒数进行序列化。不发送本地时区信息。可以使用对隐式对象引用表中的索引将日期作为先前发生的日期实例的引用发送。</p>\n\n<h4 id=\"11array-type\">11、<code class=\"language-plaintext highlighter-rouge\">Array</code> type</h4>\n\n<p>ActionScript 数组的类型和在数组中的位置是基于它们的索引性质描述的。以下表格概述了这些术语的含义:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">strict</code>:仅包含序数(数字)索引</li>\n <li><code class=\"language-plaintext highlighter-rouge\">dense</code>:序数索引从 0 开始,并且在连续索引之间不存在间隙(即,从 0 到数组长度的每一个索引都被定义了)</li>\n <li><code class=\"language-plaintext highlighter-rouge\">sparse</code>:包含至少两个索引之间的一个间隙</li>\n <li><code class=\"language-plaintext highlighter-rouge\">associative</code>:包含至少一个非序数(字符串)索引(有时称为 ECMA 数组)</li>\n</ul>\n\n<p>AMF 将数组分为两部分,密集部分和关联部分。关联部分的二进制表示由名称/值对(可能没有)终止的空字符串。密集部分的二进制表示由密集部分的大小(可能为零)以及有序的值列表(可能没有)组成。在 AMF 中写入的顺序是密集部分的大小,一个以空字符串终止的名称/值对列表,然后是大小的值。数组可以通过使用隐式对象引用表的索引作为先前发生的数组的引用来发送。</p>\n\n<h4 id=\"12object-type\">12、<code class=\"language-plaintext highlighter-rouge\">Object</code> type</h4>\n\n<p>AMF 3 中有一种类型用于处理 ActionScript 对象和自定义用户类。使用术语 “traits” 来描述类的定义特征。除了 “anonymous” 对象和 “typed” 对象,ActionScript 3.0 还引入了两个进一步的 traits 来描述如何序列化对象,即 “dynamic” 和 “externalizable”。以下表格概述了这些术语和它们的含义:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">Anonymous</code>:实际的 ActionScript 对象类型的实例或没有注册别名的类的实例(在反序列化时将其视为对象)。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Typed</code>:具有注册别名的类的实例。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Dynamic</code>:具有动态特征声明的类定义的实例;可以在运行时动态地从实例中添加和删除公共变量成员。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Externalizable</code>:实现 flash.utils.IExternalizable 的类的实例,它完全控制其成员的序列化(特征信息中不包含属性名)。</li>\n</ul>\n\n<p>在这些特征之外,对象的特征信息还可能包括在类上定义的一组公共变量和公共可读写属性名称(即不是函数的公共成员)。成员名称的顺序很重要,因为在特征信息之后的成员值将按照完全相同的顺序出现。这些成员被视为密封成员,因为它们是由类型明确定义的。</p>\n\n<p>如果类型是动态的,则在密封成员之后可以包括一个进一步的部分,该部分将动态成员列为名称/值对。当遇到空字符串名称时,继续读取动态成员。</p>\n\n<p>对象可以通过使用隐式对象引用表中的索引来作为先前发生对象的引用。此外,还可以通过使用隐式特征引用表中的索引将特征信息发送为先前发生的一组特征的引用。</p>\n\n<h4 id=\"13xml-type\">13、<code class=\"language-plaintext highlighter-rouge\">XML</code> type</h4>\n\n<p>ActionScript 3.0 引入了一种新的 <code class=\"language-plaintext highlighter-rouge\">XML</code> 类型,支持 E4X 语法。为了序列化,需要将 <code class=\"language-plaintext highlighter-rouge\">XML</code> 类型展平成字符串表示形式。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。<code class=\"language-plaintext highlighter-rouge\">XML</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 XML 实例的引用发送。请注意,这种编码对 <code class=\"language-plaintext highlighter-rouge\">XML</code> 的使用造成了一些理论限制。每个 UTF-8 编码的 <code class=\"language-plaintext highlighter-rouge\">XML</code> 实例的字节长度最大为 228-1 字节(大约 256 MB)。</p>\n\n<h4 id=\"14bytearray-type\">14、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> type</h4>\n\n<p>用于保存字节数组,即 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>。AMF 3 使用可变长度编码 29 位整数序列化此类型,其中包括字节长度前缀,然后是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 的原始字节。<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实例的引用发送。</p>\n\n<h4 id=\"15amf3-的使用\">15、AMF3 的使用</h4>\n\n<h5 id=\"151netconnection-and-amf-3\">15.1、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> and AMF 3</h5>\n\n<p>除了序列化 ActionScript 类型外,AMF 还可用于远程服务的异步调用。可使用简单的消息结构将一批请求发送到远程端点。此消息结构的格式为 AMF 0(参见[AMF0])。可以使用特殊的 <code class=\"language-plaintext highlighter-rouge\">avmplus-object-marker</code> 类型将上下文头值或消息正文切换到 AMF 3 编码。</p>\n\n<h5 id=\"152netconnection-in-actionscript-30\">15.2、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> in ActionScript 3.0</h5>\n\n<p>在 ActionScript 3.0 中,NetConnection 的限定类名是 flash.net.NetConnection。这个类仍然使用响应器来处理远程端点的结果和状态响应,但是现在需要强类型的 Responder 类。完全限定的类名是 flash.net.Responder。除了正常的结果和状态响应之外,NetConnection 还会分发事件,开发人员可以添加监听器。下面是这些事件的概述:</p>\n\n<ul>\n <li>当异常异步抛出时触发,例如来自本机异步代码。</li>\n <li>当输入或输出错误导致网络操作失败时触发。</li>\n <li>当 NetConnection 对象报告其状态或错误条件时触发。</li>\n <li>如果对 NetConnection.call() 的调用尝试连接到调用者安全沙箱外的服务器,则会触发。</li>\n</ul>\n\n<h5 id=\"153bytearray-idatainput-and-idataoutput\">15.3、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code>, <code class=\"language-plaintext highlighter-rouge\">IDataInput</code> and <code class=\"language-plaintext highlighter-rouge\">IDataOutput</code></h5>\n\n<p>ActionScript 3.0 引入了一种新类型,用于支持以字节数组形式处理原始数据,即 <code class=\"language-plaintext highlighter-rouge\">flash.utils.ByteArray</code>。为了协助 ActionScript 对象序列化和复制,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实现了 <code class=\"language-plaintext highlighter-rouge\">flash.utils.IDataInput</code> 和 <code class=\"language-plaintext highlighter-rouge\">flash.utils.IDataOutput</code>。这些接口指定了帮助将常见类型写入字节流的实用方法。两个感兴趣的方法是 <code class=\"language-plaintext highlighter-rouge\">IDataOutput.writeObject</code> 和 <code class=\"language-plaintext highlighter-rouge\">IDataInput.readObject</code>。这些方法使用 AMF 编码对象。使用的 AMF 版本由 <code class=\"language-plaintext highlighter-rouge\">ByteArray.objectEncoding</code> 方法控制,该方法可以设置为 AMF 3 或 AMF 0。枚举类型 <code class=\"language-plaintext highlighter-rouge\">flash.net.ObjectEncoding</code> 包含 AMF 版本的常量:分别为 <code class=\"language-plaintext highlighter-rouge\">ObjectEncoding.AMF0</code> 和 <code class=\"language-plaintext highlighter-rouge\">ObjectEncoding.AMF3</code>。</p>\n\n<p>请注意,<code class=\"language-plaintext highlighter-rouge\">ByteArray.writeObject</code> 使用一个版本的 AMF 对整个对象进行编码。与 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 不同,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 不会从 AMF 0 开始,然后将 <code class=\"language-plaintext highlighter-rouge\">objectEncoding</code> 属性设置为 AMF 3 并切换到 AMF 3。还请注意,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 为每个 <code class=\"language-plaintext highlighter-rouge\">readObject</code> 和 <code class=\"language-plaintext highlighter-rouge\">writeObject</code> 调用使用新的对象、对象特征和字符串的隐式引用表。</p>\n\n<h3 id=\"二binaryreaderwriter\">二、<code class=\"language-plaintext highlighter-rouge\">BinaryReader/Writer</code></h3>\n\n<h4 id=\"1amf3-数据格式基础\">1、AMF3 数据格式基础</h4>\n\n<p>首先介绍一下变长整数(Variable Length Integer),比如 UInt32 如下。</p>\n\n<p><img src=\"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png\" alt=\"image\" /></p>\n\n<p>上图摘自 Adobe AMF3 官方文档,这是一种压缩方式的整数存储,且每一字节都对后面的数据具有预知作用。那么字符串如何处理呢?下面是字符串的处理方式,AMF0 和 AMF3 都才用 UTF-8 编码方式,并做如下压缩处理:</p>\n\n<p><img src=\"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png\" alt=\"image\" /></p>\n\n<p>上图摘自 Adobe AMF3 官方文档。</p>\n\n<h4 id=\"2序列化\">2、序列化</h4>\n\n<p>序列化包括 8 位、16 位、32 位,以及 UTF-8 和 UTF-16(I guess)编码的 String,还有原生数据(Raw Data)、变长无符号整数(Variable Length Unsigned Integer)以及 IP 地址。所谓序列化就是按照指定格式编写各种对象、基础数据类型值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">BinaryWriter</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ostream</span><span class=\"o\">&</span> <span class=\"n\">ostr</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">BinaryWriter</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write7BitValue</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write7BitLongValue</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt64</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeAddress</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Address</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">,</span><span class=\"kt\">bool</span> <span class=\"n\">publicFlag</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeAddress</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Net</span><span class=\"o\">::</span><span class=\"n\">SocketAddress</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">,</span><span class=\"kt\">bool</span> <span class=\"n\">publicFlag</span><span class=\"p\">);</span>\n <span class=\"k\">static</span> <span class=\"n\">BinaryWriter</span> <span class=\"n\">BinaryWriterNull</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>请注意其中名为 <code class=\"language-plaintext highlighter-rouge\">BinaryWriterNull</code> 的成员。构造函数定义为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">ostream</span><span class=\"o\">&</span> <span class=\"n\">ostr</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">ostr</span><span class=\"p\">,</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">NETWORK_BYTE_ORDER</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n\n<span class=\"n\">BinaryWriter</span><span class=\"o\">::~</span><span class=\"n\">BinaryWriter</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">writeRaw</code> 是简单地封装 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter::writeRaw()</code>,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入整数实现如下,用的是从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 继承来的重载运算符操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span> \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入字符串:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入变长整数,这段代码含义也一目了然,就是读取变长无符号 32 位整数、64 位整数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write7BitValue</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">Get7BitValueSize</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span><span class=\"o\">*</span><span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">21</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"c1\">// 4 bytes maximum</span>\n <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"mi\">22</span><span class=\"p\">;</span>\n <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">7</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"mh\">0x80</span> <span class=\"o\">|</span> <span class=\"p\">((</span><span class=\"n\">value</span><span class=\"o\">>></span><span class=\"n\">shift</span><span class=\"p\">)</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">));</span>\n <span class=\"n\">shift</span> <span class=\"o\">-=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">max</span> <span class=\"o\">?</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0xFF</span> <span class=\"o\">:</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write7BitLongValue</span><span class=\"p\">(</span><span class=\"n\">UInt64</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">Get7BitValueSize</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span><span class=\"o\">*</span><span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">63</span><span class=\"p\">;</span> <span class=\"c1\">// Can give 10 bytes!</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">max</span><span class=\"p\">)</span>\n <span class=\"o\">++</span><span class=\"n\">shift</span><span class=\"p\">;</span>\n \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">7</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"mh\">0x80</span> <span class=\"o\">|</span> <span class=\"p\">((</span><span class=\"n\">value</span><span class=\"o\">>></span><span class=\"n\">shift</span><span class=\"p\">)</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">));</span>\n <span class=\"n\">shift</span> <span class=\"o\">-=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">max</span> <span class=\"o\">?</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0xFF</span> <span class=\"o\">:</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入 IP 地址的两个函数暂略。</p>\n\n<h4 id=\"3反序列化\">3、反序列化</h4>\n\n<p>反序列化就是从指定格式的数据中读出各类型的数据值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">BinaryReader</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">istream</span><span class=\"o\">&</span> <span class=\"n\">istr</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">BinaryReader</span><span class=\"p\">();</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt64</span> <span class=\"n\">read7BitLongValue</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read7BitEncoded</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString8</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString16</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">read16</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read32</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readAddress</span><span class=\"p\">(</span><span class=\"n\">Address</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">);</span>\n \n <span class=\"k\">static</span> <span class=\"n\">BinaryReader</span> <span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>构造与析构函数都很简单:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">istream</span><span class=\"o\">&</span> <span class=\"n\">istr</span><span class=\"p\">)</span> <span class=\"o\">:</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">istr</span><span class=\"p\">,</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">NETWORK_BYTE_ORDER</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">BinaryReader</span><span class=\"o\">::~</span><span class=\"n\">BinaryReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读取原生数据(Raw Data):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写整数,用的是 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter</code> 的重载运算符:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读写整数依旧使用从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 继承来的运算符操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt8</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read8</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">UInt16</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read16</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">UInt32</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read32</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写字符串:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读取变长整数,分别针对 <code class=\"language-plaintext highlighter-rouge\">UInt32</code> 和 <code class=\"language-plaintext highlighter-rouge\">UInt64</code>,要理解 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 的变长整数才能理解这个:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x80</span><span class=\"p\">)</span> <span class=\"o\">&&</span> <span class=\"n\">n</span> <span class=\"o\"><</span> <span class=\"mi\">3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"p\">(</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"o\">++</span><span class=\"n\">n</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"p\">((</span><span class=\"n\">n</span><span class=\"o\"><</span><span class=\"mi\">3</span><span class=\"p\">)</span> <span class=\"o\">?</span> <span class=\"mi\">7</span> <span class=\"o\">:</span> <span class=\"mi\">8</span><span class=\"p\">);</span> <span class=\"c1\">// Use all 8 bits from the 4th byte</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"n\">b</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt64</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read7BitLongValue</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">UInt64</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x80</span><span class=\"p\">)</span> <span class=\"o\">&&</span> <span class=\"n\">n</span> <span class=\"o\"><</span> <span class=\"mi\">8</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"p\">(</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"o\">++</span><span class=\"n\">n</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"p\">((</span><span class=\"n\">n</span><span class=\"o\"><</span><span class=\"mi\">8</span><span class=\"p\">)</span> <span class=\"o\">?</span> <span class=\"mi\">7</span> <span class=\"o\">:</span> <span class=\"mi\">8</span><span class=\"p\">);</span> <span class=\"c1\">// Use all 8 bits from the 4th byte</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"n\">b</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三packetreaderwriter\">三、<code class=\"language-plaintext highlighter-rouge\">PacketReader/Writer</code></h3>\n\n<h4 id=\"1packetreader\">1、PacketReader</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#define PACKETRECV_SIZE 2048\nclass PacketReader: public BinaryReader {\npublic:\n PacketReader(const Poco::UInt8* buffer,Poco::UInt32 size);\n PacketReader(PacketReader&);\n virtual ~PacketReader();\n const Poco::UInt32 fragments;\n Poco::UInt32 available(); // 可读字节数\n Poco::UInt8* current();\n Poco::UInt32 position(); // 获取当前的相对位置(相对于起始位置的)\n void reset(Poco::UInt32 newPos = 0); // 设定当前位置\n void shrink(Poco::UInt32 rest);\n void next(Poco::UInt32 size);\nprivate:\n MemoryInputStream _memory;\n};\n</code></pre></div></div>\n\n<h6 id=\"11封装-memoryinputstream\">1.1、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">current</code>:当前绝对位置(内存地址)</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code>:当前位置(绝对位置)减去缓冲区起始位置</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"12收缩缓冲区\">1.2、收缩缓冲区</h6>\n\n<p>封装了 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 的 <code class=\"language-plaintext highlighter-rouge\">resize</code>。不过由于前面的 <code class=\"language-plaintext highlighter-rouge\">if</code> 语句影响,传给 resize 的参数一定不会大于缓冲区的当前大小。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">shrink</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">rest</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">rest</span> <span class=\"o\">></span> <span class=\"n\">available</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"rest %u more upper than available %u bytes\"</span><span class=\"p\">,</span><span class=\"n\">rest</span><span class=\"p\">,</span><span class=\"n\">available</span><span class=\"p\">());</span>\n <span class=\"n\">rest</span> <span class=\"o\">=</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">rest</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"13构造函数拷贝构造函数和析构函数\">1.3、构造函数、拷贝构造函数和析构函数</h6>\n\n<p>构造函数先调用父类 <code class=\"language-plaintext highlighter-rouge\">BinaryReader</code> 的构造函数,并初始化 <code class=\"language-plaintext highlighter-rouge\">fragments</code> 和 <code class=\"language-plaintext highlighter-rouge\">_memory</code> 输入流的缓冲区。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">PacketReader</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">fragments</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"c1\">// Consctruction by copy</span>\n<span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">PacketReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">fragments</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">fragments</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">PacketReader</span><span class=\"o\">::~</span><span class=\"n\">PacketReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2packetwriter\">2、<code class=\"language-plaintext highlighter-rouge\">PacketWriter</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">PacketWriter</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">BinaryWriter</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"n\">PacketWriter</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">PacketWriter</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">good</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">clear</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">limit</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"n\">MemoryOutputStream</span> <span class=\"n\">_memory</span><span class=\"p\">;</span>\n <span class=\"n\">PacketWriter</span><span class=\"o\">*</span> <span class=\"n\">_pOther</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h6 id=\"21封装memoryoutputstream\">2.1、封装<code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">good</code>:不过 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code> 也是封装的 <code class=\"language-plaintext highlighter-rouge\">std::ostream</code> 的 <code class=\"language-plaintext highlighter-rouge\">good</code> 函数,True if no error flags are set.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">bool</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">good</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">good</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">written</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">length</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code>:设置缓冲区的指针位置,即 <code class=\"language-plaintext highlighter-rouge\">position</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code>:移动缓冲区指针</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin</code>:返回缓冲区的起始地址</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">clear</code>:其实就是修改 written 和 position,使得指定位置后面的数据在以后写的时候可以被覆盖,并不是真正的清除。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">clear</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">limit</code>:封装 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code> 的 <code class=\"language-plaintext highlighter-rouge\">resize</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">limit</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">length</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">length</span> <span class=\"o\">=</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">length</span> <span class=\"o\">></span> <span class=\"n\">_size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"Limit '%d' more upper than buffer size '%d' bytes\"</span><span class=\"p\">,</span><span class=\"n\">length</span><span class=\"p\">,</span><span class=\"n\">_size</span><span class=\"p\">);</span>\n <span class=\"n\">length</span> <span class=\"o\">=</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">length</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"22封装-binarywriter\">2.2、封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">flush</code>:封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code> 的 <code class=\"language-plaintext highlighter-rouge\">flush</code>,不过 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code> 的 <code class=\"language-plaintext highlighter-rouge\">flush</code> 实际上是从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter</code> 继承而来的。其作用是「Flushes the underlying stream」。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">flush</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_pOther</span> <span class=\"o\">&&</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"n\">_pOther</span><span class=\"o\">-></span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">())</span>\n <span class=\"n\">_pOther</span><span class=\"o\">-></span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">());</span>\n <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">flush</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"23构造函数拷贝构造函数和析构函数\">2.3、构造函数、拷贝构造函数和析构函数</h6>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">_pOther</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_size</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"c1\">// Consctruction by copy</span>\n<span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"n\">PacketWriter</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_pOther</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">other</span><span class=\"p\">),</span>\n <span class=\"n\">_memory</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">_size</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>注意析构函数中会进行 <code class=\"language-plaintext highlighter-rouge\">flush</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketWriter</span><span class=\"o\">::~</span><span class=\"n\">PacketWriter</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"四amfreader\">四、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code></h3>\n\n<h4 id=\"1objectdef\">1、<code class=\"language-plaintext highlighter-rouge\">ObjectDef</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ObjectDef</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span> \n <span class=\"n\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">amf3</span><span class=\"p\">(</span><span class=\"n\">amf3</span><span class=\"p\">),</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">dynamic</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">externalizable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">count</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">arrayType</span><span class=\"p\">(</span><span class=\"n\">arrayType</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">string</span><span class=\"o\">></span> <span class=\"n\">hardProperties</span><span class=\"p\">;</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">reset</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">dynamic</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">externalizable</span><span class=\"p\">;</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">count</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"p\">;</span>\n <span class=\"k\">const</span> <span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"2amfreader-定义\">2、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 定义</h4>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 作为其成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">AMFReader</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">AMFReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">AMFReader</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">readSimpleObject</span><span class=\"p\">(</span><span class=\"n\">AMFSimpleObject</span><span class=\"o\">&</span> <span class=\"n\">object</span><span class=\"p\">);</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">read</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">double</span> <span class=\"n\">readNumber</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Int32</span> <span class=\"n\">readInteger</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readBoolean</span><span class=\"p\">();</span>\n <span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">readByteArray</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">&</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Timestamp</span> <span class=\"n\">readDate</span><span class=\"p\">();</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">readObject</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readArray</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readDictionary</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">weakKeys</span><span class=\"p\">);</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readKey</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readValue</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readItem</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">);</span>\n <span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">readRawObjectContent</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">readNull</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">startReferencing</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">stopReferencing</span><span class=\"p\">();</span>\n \n <span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">;</span>\n \n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">ObjectDef</span><span class=\"o\">*></span> <span class=\"n\">_objectDefs</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_stringReferences</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_classDefReferences</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_amf0References</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_amf0Reset</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_reset</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_amf3</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">_referencing</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"21构造函数析构函数\">2.1、构造函数、析构函数</h5>\n\n<p>参数为 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code>,会初始化一些成员变量。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">AMFReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">reader</span><span class=\"p\">(</span><span class=\"n\">reader</span><span class=\"p\">),</span>\n <span class=\"n\">_reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_amf3</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_amf0Reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_referencing</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构时,会逐一释放 <code class=\"language-plaintext highlighter-rouge\">_objectDefs</code> 中对象的内存:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMFReader</span><span class=\"o\">::~</span><span class=\"n\">AMFReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">ObjectDef</span><span class=\"o\">*>::</span><span class=\"n\">iterator</span> <span class=\"n\">it</span><span class=\"p\">;</span>\n <span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span>\n <span class=\"k\">delete</span> <span class=\"o\">*</span><span class=\"n\">it</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22简单封装-packetreader-的一些函数\">2.2、简单封装 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 的一些函数</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code>:操作指针位置</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_reset</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"p\">);</span>\n <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code>:根据当前缓冲区大小和 <code class=\"language-plaintext highlighter-rouge\">written</code> 计算得到</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">current</code>:<code class=\"language-plaintext highlighter-rouge\">gptr</code> 内存地址</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">*</span><span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23设置-gptr-位置\">2.3、设置 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 位置</h5>\n\n<p>其实 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 也被影响了,但是在 <code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 中只用 <code class=\"language-plaintext highlighter-rouge\">gptr</code>。调用构造函数的时候,<code class=\"language-plaintext highlighter-rouge\">reset</code> 被设为 0,其后在每次读取数据的时候都会影响 <code class=\"language-plaintext highlighter-rouge\">reset</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"p\">);</span>\n <span class=\"n\">_reset</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"24判断类型\">2.4、判断类型</h5>\n\n<p>分析请看注释:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">followingType</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先 <code class=\"language-plaintext highlighter-rouge\">reset</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span> <span class=\"o\">!=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">back</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">amf3</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>是 AMF0 类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>如果没有可读数据了,则返回 AMF::End。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">available</span><span class=\"p\">())</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">End</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>开始读了,先读到的表示 AMF 数据类型。要注意的是调用 current 并不改变指针的位置,所以你会在线面看到调用 next。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt8</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_amf3</span> <span class=\"o\">&&</span> <span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF_AVMPLUS_OBJECT</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">available</span><span class=\"p\">())</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">End</span><span class=\"p\">;</span>\n <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>AMF3 类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">switch</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>Undefined 和 null 都当做 null。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF3_UNDEFINED</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_NULL</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>false 和 true 都是 boolean。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF3_FALSE</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_TRUE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_INTEGER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Integer</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_NUMBER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">String</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_DATE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Array</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_DICTIONARY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Dictionary</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Object</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_BYTEARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">ByteArray</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>落到 default 手里的话,就跳过这个字节,读取下一个。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"nl\">default:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Unknown AMF3 type %.2x\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>AMF0 类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">switch</span> <span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">undefined</code> 和 <code class=\"language-plaintext highlighter-rouge\">null</code> 都是 <code class=\"language-plaintext highlighter-rouge\">null</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_UNDEFINED</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_NULL</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">;</span>\n \n <span class=\"k\">case</span> <span class=\"n\">AMF_BOOLEAN</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_NUMBER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">long string</code> 和 <code class=\"language-plaintext highlighter-rouge\">string</code> 都是 <code class=\"language-plaintext highlighter-rouge\">string</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_LONG_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">String</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">mixed array</code> 和 <code class=\"language-plaintext highlighter-rouge\">strict array</code> 都是 <code class=\"language-plaintext highlighter-rouge\">array</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_MIXED_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_STRICT_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Array</span><span class=\"p\">;</span>\n \n <span class=\"k\">case</span> <span class=\"n\">AMF_DATE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin object</code> 和 <code class=\"language-plaintext highlighter-rouge\">begin typed object</code> 都是 <code class=\"language-plaintext highlighter-rouge\">object</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_BEGIN_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_BEGIN_TYPED_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Object</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是引用,就跳过表示类型值的这个字节。这个先留下来,带我们分析完 readArray 和 readObject 再回头看。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_REFERENCE</span><span class=\"p\">:</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read16</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">reference</span> <span class=\"o\">></span> <span class=\"n\">_amf0References</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF0 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_amf0Reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_amf0References</span><span class=\"p\">[</span><span class=\"n\">reference</span><span class=\"p\">]);</span>\n <span class=\"k\">return</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果没了,或者不支持,或者都不是,就跳过这个字节,递归继续读取:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_END_OBJECT</span><span class=\"p\">:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF end object type without begin object type before\"</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_UNSUPPORTED</span><span class=\"p\">:</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"Unsupported type in AMF format\"</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">default</span><span class=\"o\">:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Unknown AMF type %.2x\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">followingType</code> 是这个类的核心,每个具体的数据类型的分析都依赖于它的判断。这些类型的解析,会在下一篇文章中介绍。</p>\n\n<h4 id=\"3解析-as3-null\">3、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Null</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readNull</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先 reset 一下是惯例,就像糗百上的割一样。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span> \n</code></pre></div></div>\n\n<p>AMF 数据类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">Null</code>,跳过该字节,并返回</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>判断错误</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Null type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4解析-as3-number\">4、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Number</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">double</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readNumber</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Null</code> 会被悲催的跳过:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">Number</code> 呀 :(</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Number type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过该字节吧</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>返回吧,返回之前还用到 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 的运算符,注意 AS3 中的 <code class=\"language-plaintext highlighter-rouge\">Number</code> 就是 C++ 的 <code class=\"language-plaintext highlighter-rouge\">double</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">double</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5解析-as3-integer\">5、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Integer</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Int32</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readInteger</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code> 类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>Null 的话:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">Integer</code> 或者 Number 的话。。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Integer</span> <span class=\"o\">&&</span> <span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Integer type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过吧。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>终于是 <code class=\"language-plaintext highlighter-rouge\">Number</code> 了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kt\">double</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Int32</span><span class=\"p\">)</span><span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读一个变长的 32 位无符号整数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Forced in AMF3 here!</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">value</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果大于 3.5 个字节所能表示的最大无符号整数值(<code class=\"language-plaintext highlighter-rouge\">268435455</code> 是 <code class=\"language-plaintext highlighter-rouge\">0xFFFFFFF</code>),则减去 <code class=\"language-plaintext highlighter-rouge\">0x2FFFFFFF</code>(这还不是太理解,有能解释的朋友给留个言,或者发 email 给我 )</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">></span> <span class=\"mi\">268435455</span><span class=\"p\">)</span>\n <span class=\"n\">value</span> <span class=\"o\">-=</span> <span class=\"p\">(</span><span class=\"mi\">1</span> <span class=\"o\"><<</span> <span class=\"mi\">29</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"6解析-as3-boolean\">6、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Boolean</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readBoolean</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">Null</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>居然不是 <code class=\"language-plaintext highlighter-rouge\">Boolean</code> 的话。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Boolean type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 的话,返回 <code class=\"language-plaintext highlighter-rouge\">true</code> 或者 <code class=\"language-plaintext highlighter-rouge\">false</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span><span class=\"o\">==</span> <span class=\"n\">AMF3_FALSE</span> <span class=\"o\">?</span> <span class=\"nb\">false</span> <span class=\"o\">:</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 就是 <code class=\"language-plaintext highlighter-rouge\">AMF0</code> 喽:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span><span class=\"o\">==</span><span class=\"mh\">0x00</span> <span class=\"o\">?</span> <span class=\"nb\">false</span> <span class=\"o\">:</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"7开始引用与结束引用\">7、开始引用与结束引用</h4>\n\n<p>如下这两个函数会在 <code class=\"language-plaintext highlighter-rouge\">FlowConnection</code> 中调用。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">startReferencing</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_referencing</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">stopReferencing</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_referencing</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"8解析-as3-bytearray\">8、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code></h4>\n\n<p>先回顾一下 AMF3 中的ByteArray 的数据格式:</p>\n\n<p>注意到,首先要读取一个变长无符号 32 位整数,但是最低位是 1,只有 28 位用于表示数据长度。解释完这里,下面的解析过程才好理解。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readByteArray</span><span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Null</code> 就返回 <code class=\"language-plaintext highlighter-rouge\">BinaryReaderNull</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>,也返回 <code class=\"language-plaintext highlighter-rouge\">BinaryReaderNull</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">ByteArray</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF ByteArray type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过这个字节:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>注意 position 返回的是相对位置,与 AS3 中一样。<code class=\"language-plaintext highlighter-rouge\">reference</code> 表示这个地址(简单说,引用就是地址嘛)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>读取一个变长 32 位无符号整数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>最低位是 1 的话,<code class=\"language-plaintext highlighter-rouge\">isInline</code> 是 <code class=\"language-plaintext highlighter-rouge\">true</code>,否则为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">size</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>右移一位,因为那一位是标志位,上面解释过了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">isInline</code> 是 <code class=\"language-plaintext highlighter-rouge\">true</code>,表示是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">_referencing</code> 为 <code class=\"language-plaintext highlighter-rouge\">true</code> 的话(这是一个 <code class=\"language-plaintext highlighter-rouge\">vector</code>),push back this reference:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>不符合 ByteArray 的格式定义的话:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">size</span> <span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>移动到这个 reference 的位置,<code class=\"language-plaintext highlighter-rouge\">_references[size]</code> 就是这个位置(相对)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">size</span><span class=\"p\">]);</span> <span class=\"c1\">// TODO size 作为索引,还没有完全理解</span>\n</code></pre></div></div>\n\n<p>读取这个 reference 的 size 值给 size对象(注意 size 是这个函数传入的引用参数,其值可以被修改)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"o\">>></span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>把读取完 ByteArraty 的 PacketReader 返回:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>最后强调一点,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 的数据段最大长度为 228 -1 字节,约为 256 MB。</p>\n\n<h4 id=\"9解析-as3-date\">9、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Date</code></h4>\n\n<p>先看下 <code class=\"language-plaintext highlighter-rouge\">Date</code> 的数据格式:</p>\n\n<p>下面开始分析:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Timestamp</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readDate</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>Null 的话,就返回当前时间:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是 Date 类型,也返回当前时间:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Date type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"kt\">double</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是 AMF3:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先读取 flag,最低一位必须是 1,其他位丢到垃圾桶。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">flags</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>当前相对位置。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>是 1 就 push back 到 <code class=\"language-plaintext highlighter-rouge\">_references</code> 里。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">flags</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>读取一个 double,到 result 里(result 也是 double 类型哦~)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>如果标志位不是 1,麻烦不少哒。。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">flags</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果 flag 超了,就返回当前时间作为时间戳作为 Date。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">flags</span> <span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这段与 ByteArray 那段一样:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">flags</span><span class=\"p\">]);</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>返回喽~</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"nf\">Timestamp</span><span class=\"p\">((</span><span class=\"n\">Timestamp</span><span class=\"o\">::</span><span class=\"n\">TimeVal</span><span class=\"p\">)</span> <span class=\"n\">result</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>读俩,因为是 double(64 位):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">2</span><span class=\"p\">);</span> <span class=\"c1\">// Timezone, useless</span>\n</code></pre></div></div>\n\n<p>返回喽~</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"nf\">Timestamp</span><span class=\"p\">((</span><span class=\"n\">Timestamp</span><span class=\"o\">::</span><span class=\"n\">TimeVal</span><span class=\"p\">)</span> <span class=\"n\">result</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"10解析-as3-dictionary\">10、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Dictionary</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readDictionary</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">weakKeys</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>下面这段咱就略了。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Dictionary</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Dictionary type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过 type:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// AMF3</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span> <span class=\"c1\">// marker</span>\n</code></pre></div></div>\n\n<p>当前相对位置值作为 <code class=\"language-plaintext highlighter-rouge\">reference</code>,再读个 <code class=\"language-plaintext highlighter-rouge\">size</code>,还是最低位必须为 1,不是就返回 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">size</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n <span class=\"n\">size</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">isInline</span> <span class=\"o\">&&</span> <span class=\"n\">size</span><span class=\"o\">></span><span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>下面要调用到 <code class=\"language-plaintext highlighter-rouge\">ObjectRef</code> 构造函数,这里再把其实现拿出来看看,其实主要是初始化了哪些成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">amf3</span><span class=\"p\">(</span><span class=\"n\">amf3</span><span class=\"p\">),</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">dynamic</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">externalizable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">count</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">arrayType</span><span class=\"p\">(</span><span class=\"n\">arrayType</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>可以看到要有一个 amf3,还有 <code class=\"language-plaintext highlighter-rouge\">reset</code> 置为 0,<code class=\"language-plaintext highlighter-rouge\">dynamic</code> 置为 <code class=\"language-plaintext highlighter-rouge\">false</code>,<code class=\"language-plaintext highlighter-rouge\">externalizable</code> 也是 <code class=\"language-plaintext highlighter-rouge\">false</code>,<code class=\"language-plaintext highlighter-rouge\">count</code> 是 0,<code class=\"language-plaintext highlighter-rouge\">arrayType</code> 成员要赋值。</p>\n\n<p>上面是插播哦,下面还要继续哒。创建这么一个对象,注意是 new 出来的,所以我们在《OpenRTMFP/Cumulus Primer(16)AMF解析之AMFReader》一文中提到了 AMFReader 的析构函数中要对 <code class=\"language-plaintext highlighter-rouge\">_objectRef</code> 的每个元素逐一析构的。<code class=\"language-plaintext highlighter-rouge\">arrayType</code> 就设置为 <code class=\"language-plaintext highlighter-rouge\">AMF3_DICTIONARY</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ObjectDef</span><span class=\"o\">*</span> <span class=\"n\">pObjectDef</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nf\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">,</span> <span class=\"n\">AMF3_DICTIONARY</span><span class=\"p\">);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">dynamic</span><span class=\"o\">=</span><span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pObjectDef</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果标志位是 1,就直接 push back,跟之前一样。不过这里多了一个 <code class=\"language-plaintext highlighter-rouge\">pObjectDef</code>,所以还要设置一下它的计数为 <code class=\"language-plaintext highlighter-rouge\">size</code>,就是 <code class=\"language-plaintext highlighter-rouge\">dictionary</code> 数据大小。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">=</span> <span class=\"n\">size</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果标志位是 0,就把 <code class=\"language-plaintext highlighter-rouge\">count</code> 设置为下一个变长整数值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">size</span><span class=\"p\">]);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"o\">>></span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">*=</span> <span class=\"mi\">2</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>读一个字节,如果最小位是 1,weakKeys 就是 true,否则为 false。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">weakKeys</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n \n <span class=\"k\">return</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</title>\n \t<meta name=\"description\" content=\"CumulusServer 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 CumulusEdge 和 CumulusServer 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 CumulusServer 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</h2>\t\t\n\t<time datetime=\"2012-04-15T14:26:58+00:00\" class=\"by-line\">15 Apr 2012, 广州 | 作者 麦克船长 | 总计 3844 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#1绑定地址\" id=\"markdown-toc-1绑定地址\">1、绑定地址</a></li>\n <li><a href=\"#2cumulusserver-接收数据\" id=\"markdown-toc-2cumulusserver-接收数据\">2、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 接收数据</a></li>\n <li><a href=\"#3如果-cumulusedge-端口存在且-edge-socket-可用\" id=\"markdown-toc-3如果-cumulusedge-端口存在且-edge-socket-可用\">3、如果 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 端口存在且 edge socket 可用。</a></li>\n <li><a href=\"#4cumulusserver-和-cumulusedge-的-socket-都没有数据\" id=\"markdown-toc-4cumulusserver-和-cumulusedge-的-socket-都没有数据\">4、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 都没有数据:</a></li>\n <li><a href=\"#5发送方的-ip-被禁\" id=\"markdown-toc-5发送方的-ip-被禁\">5、发送方的 ip 被禁:</a></li>\n <li><a href=\"#6数据包长度小于可能的最小值12\" id=\"markdown-toc-6数据包长度小于可能的最小值12\">6、数据包长度小于可能的最小值(12)</a></li>\n</ul>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。</p>\n\n<p>本所要介绍的这个主循环在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(const volatile bool& terminate)</code> 函数中。RTMFPServer覆盖 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的 <code class=\"language-plaintext highlighter-rouge\">run(const volatile bool &terminate)</code> 方法。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<h3 id=\"1绑定地址\">1、绑定地址</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 IP 地址和端口:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"nf\">address</span><span class=\"p\">(</span><span class=\"s\">\"0.0.0.0\"</span><span class=\"p\">,</span><span class=\"n\">_port</span><span class=\"p\">);</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">address</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n<span class=\"err\">绑定</span><span class=\"n\">CumulusEdge</span><span class=\"err\">的</span> <span class=\"n\">IP</span> <span class=\"err\">地址和端口:</span>\n\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"nf\">edgesAddress</span><span class=\"p\">(</span><span class=\"s\">\"0.0.0.0\"</span><span class=\"p\">,</span><span class=\"n\">_edgesPort</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">edgesAddress</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>发送者(Client)的 IP 地址和端口:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"n\">sender</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">buff</span><span class=\"p\">[</span><span class=\"n\">PACKETRECV_SIZE</span><span class=\"p\">];</span>\n <span class=\"kt\">int</span> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n \n <span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">stop</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">idle</span> <span class=\"o\">=</span> <span class=\"n\">realTime</span><span class=\"p\">(</span><span class=\"n\">stop</span><span class=\"p\">);</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">stop</span><span class=\"p\">)</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n \n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h3 id=\"2cumulusserver-接收数据\">2、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 接收数据</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 有数据可读:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>从 <code class=\"language-plaintext highlighter-rouge\">socket</code> 读取:把数据存到 <code class=\"language-plaintext highlighter-rouge\">buff</code>,把发送者地址赋给 <code class=\"language-plaintext highlighter-rouge\">sender</code>,把所读长度返回给 <code class=\"language-plaintext highlighter-rouge\">size</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">receiveFrom</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span><span class=\"k\">sizeof</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">),</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>处理 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 产生的异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Main socket reception : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">address</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"3如果-cumulusedge-端口存在且-edge-socket-可用\">3、如果 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 端口存在且 edge socket 可用。</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 有数据可读:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span> <span class=\"o\">></span> <span class=\"mi\">0</span> <span class=\"o\">&&</span> <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">receiveFrom</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span> <span class=\"k\">sizeof</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">),</span> <span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Main socket reception : %s\"</span><span class=\"p\">,</span> <span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">edgesAddress</span><span class=\"p\">,</span> <span class=\"nb\">true</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">Edge</span><span class=\"o\">*</span> <span class=\"n\">pEdge</span> <span class=\"o\">=</span> <span class=\"n\">edges</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pEdge</span><span class=\"p\">)</span>\n <span class=\"n\">pEdge</span><span class=\"o\">-></span><span class=\"n\">update</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<h3 id=\"4cumulusserver-和-cumulusedge-的-socket-都没有数据\">4、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 都没有数据:</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 空闲:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">idle</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>主线程等待一秒。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_timeLastManage</span><span class=\"p\">.</span><span class=\"n\">isElapsed</span><span class=\"p\">(</span><span class=\"n\">_freqManage</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>Just middle session</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_middle</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Sessions</span><span class=\"o\">::</span><span class=\"n\">Iterator</span> <span class=\"n\">it</span><span class=\"p\">;</span>\n <span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span> <span class=\"o\">!=</span> <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Middle</span><span class=\"o\">*</span> <span class=\"n\">pMiddle</span> <span class=\"o\">=</span> <span class=\"k\">dynamic_cast</span><span class=\"o\"><</span><span class=\"n\">Middle</span><span class=\"o\">*></span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMiddle</span><span class=\"p\">)</span>\n <span class=\"n\">pMiddle</span><span class=\"o\">-></span><span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_timeLastManage</span><span class=\"p\">.</span><span class=\"n\">update</span><span class=\"p\">();</span>\n <span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"5发送方的-ip-被禁\">5、发送方的 ip 被禁:</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isBanned</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">.</span><span class=\"n\">host</span><span class=\"p\">()))</span> <span class=\"p\">{</span>\n <span class=\"n\">INFO</span><span class=\"p\">(</span><span class=\"s\">\"Data rejected because client %s is banned\"</span><span class=\"p\">,</span>\n <span class=\"n\">sender</span><span class=\"p\">.</span><span class=\"n\">host</span><span class=\"p\">().</span><span class=\"n\">toString</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"6数据包长度小于可能的最小值12\">6、数据包长度小于可能的最小值(12)</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">size</span> <span class=\"o\"><</span> <span class=\"n\">RTMFP_MIN_PACKET_SIZE</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Invalid packet\"</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">PacketReader</span> <span class=\"nf\">packet</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Session</span><span class=\"o\">*</span> <span class=\"n\">pSession</span> <span class=\"o\">=</span> <span class=\"n\">findSession</span><span class=\"p\">(</span><span class=\"n\">RTMFP</span><span class=\"o\">::</span><span class=\"n\">Unpack</span><span class=\"p\">(</span><span class=\"n\">packet</span><span class=\"p\">));</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">pSession</span><span class=\"p\">)</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">checked</span><span class=\"p\">)</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">commitCookie</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pSession</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>给 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 或者自己(<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>)的 <code class=\"language-plaintext highlighter-rouge\">socket</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">setEndPoint</span><span class=\"p\">(</span><span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span> <span class=\"o\">?</span> <span class=\"n\">_edgesSocket</span> <span class=\"o\">:</span> <span class=\"n\">_socket</span><span class=\"p\">,</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">receive</span><span class=\"p\">(</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_pCirrus</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">delete</span> <span class=\"n\">_pCirrus</span><span class=\"p\">;</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"nb\">NULL</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的第二篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</h2>\t\t\n\t<time datetime=\"2012-04-14T11:20:46+00:00\" class=\"by-line\">14 Apr 2012, 广州 | 作者 麦克船长 | 总计 18890 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一cumulus-启动源码分析\" id=\"markdown-toc-一cumulus-启动源码分析\">一、Cumulus 启动源码分析</a> <ul>\n <li><a href=\"#1maincpp-中的-main-函数\" id=\"markdown-toc-1maincpp-中的-main-函数\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数</a></li>\n <li><a href=\"#2maincpp-中的-cumulusserver-构造函数\" id=\"markdown-toc-2maincpp-中的-cumulusserver-构造函数\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer()</code> 构造函数</a></li>\n <li><a href=\"#3maincpp-中的-cumulusserver-的-initialize-成员函数\" id=\"markdown-toc-3maincpp-中的-cumulusserver-的-initialize-成员函数\">3、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 成员函数</a></li>\n <li><a href=\"#4maincpp-中的-cumulusserver-的-main-成员函数\" id=\"markdown-toc-4maincpp-中的-cumulusserver-的-main-成员函数\">4、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 成员函数</a></li>\n <li><a href=\"#5cumulusserver-是-serverapplication-的子类\" id=\"markdown-toc-5cumulusserver-是-serverapplication-的子类\">5、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的子类</a></li>\n <li><a href=\"#6serverapplication-是-application-的子类\" id=\"markdown-toc-6serverapplication-是-application-的子类\">6、<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的子类</a></li>\n <li><a href=\"#7反初始化\" id=\"markdown-toc-7反初始化\">7、反初始化</a></li>\n </ul>\n </li>\n <li><a href=\"#二cumulusserver-各项交互功能的源码解读\" id=\"markdown-toc-二cumulusserver-各项交互功能的源码解读\">二、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 各项交互功能的源码解读</a> <ul>\n <li><a href=\"#1命令行选项设定\" id=\"markdown-toc-1命令行选项设定\">1、命令行选项设定</a></li>\n <li><a href=\"#2处理命令行选项\" id=\"markdown-toc-2处理命令行选项\">2、处理命令行选项</a></li>\n <li><a href=\"#6dump-logs\" id=\"markdown-toc-6dump-logs\">6、Dump logs</a></li>\n <li><a href=\"#3停止运行\" id=\"markdown-toc-3停止运行\">3、停止运行</a></li>\n <li><a href=\"#4载入配置\" id=\"markdown-toc-4载入配置\">4、载入配置</a></li>\n <li><a href=\"#5处理日志\" id=\"markdown-toc-5处理日志\">5、处理日志</a></li>\n </ul>\n </li>\n <li><a href=\"#三maincpp-的-main-函数源码分析\" id=\"markdown-toc-三maincpp-的-main-函数源码分析\">三、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数源码分析</a> <ul>\n <li><a href=\"#1maincpp-中的-main-函数中的-server\" id=\"markdown-toc-1maincpp-中的-main-函数中的-server\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中的 <code class=\"language-plaintext highlighter-rouge\">server</code></a></li>\n <li><a href=\"#2maincpp-中-main-函数的-serverstart\" id=\"markdown-toc-2maincpp-中-main-函数的-serverstart\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的 <code class=\"language-plaintext highlighter-rouge\">server.start()</code></a></li>\n <li><a href=\"#3回顾一下整个启动流程\" id=\"markdown-toc-3回顾一下整个启动流程\">3、回顾一下整个启动流程</a></li>\n <li><a href=\"#4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\" id=\"markdown-toc-4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>、<code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(...)</code> 函数源码</a></li>\n </ul>\n </li>\n</ul>\n\n<p>本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。首先要知道的是,OpenRTMFP/Cumulus 中使用到的库有 Poco、OpenSSL 和 Lua。</p>\n\n<h3 id=\"一cumulus-启动源码分析\">一、Cumulus 启动源码分析</h3>\n\n<h4 id=\"1maincpp-中的-main-函数\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数</h4>\n\n<p>入口在 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"kt\">int</span> <span class=\"n\">argc</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">argv</span><span class=\"p\">[])</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先检查内存泄露,不过目前这个开发中的项目还没有实现这个功能,只是个空函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">DetectMemoryLeak</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>然后会创建一个匿名的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 对象,并调用其 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,该函数由 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 从 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 中继承而来,而 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 由是从 <code class=\"language-plaintext highlighter-rouge\">Application</code> 继承而来的。<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 对象调用 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,实际是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数则是调用 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的函数,而该 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数则是先调用 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数,然后调用 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,然后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数。如果 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数被调用时抛出异常,则不会执行 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,但仍然会执行 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Runs the application by performing additional initializations</span>\n <span class=\"c1\">// and calling the main() method.</span>\n <span class=\"k\">return</span> <span class=\"nf\">CumulusServer</span><span class=\"p\">().</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"n\">argc</span><span class=\"p\">,</span> <span class=\"n\">argv</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2maincpp-中的-cumulusserver-构造函数\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer()</code> 构造函数</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的构造函数定义为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">CumulusServer</span><span class=\"p\">()</span><span class=\"o\">:</span> <span class=\"n\">_helpRequested</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span> <span class=\"c1\">// 显示帮助信息</span>\n <span class=\"n\">_pCirrus</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_middle</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">_isInteractive</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_pLogFile</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3maincpp-中的-cumulusserver-的-initialize-成员函数\">3、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 成员函数</h4>\n\n<p>在执行 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数之前,<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 会启动 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数,传入的参数就是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 自己,可以猜到 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> 的 <code class=\"language-plaintext highlighter-rouge\">run</code> 方法中,调用该函数时的参数是 <code class=\"language-plaintext highlighter-rouge\">this</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">initialize</span><span class=\"p\">(</span><span class=\"n\">Application</span><span class=\"o\">&</span> <span class=\"n\">self</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>调用父函数 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的初始化函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">initialize</span><span class=\"p\">(</span><span class=\"n\">self</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>再继续下面的源码分析之前,先要知道,根据 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> ,<code class=\"language-plaintext highlighter-rouge\">Application</code> 类有一些内置的配置属性,如下:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">application.path</code>: 可执行文件的绝对路径;</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.name</code>: 可执行文件的文件名(含扩展名);</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.baseName</code>: 可执行文件的文件名(不含扩展名)</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.dir</code>: 可执行文件的所在目录;</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.configDir</code>: 配置文件所在目录;</li>\n</ul>\n\n<p>所以下面就读取了可执行文件的所在目录,其中第二个参数表示默认值(即当前目录):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">string</span> <span class=\"n\">dir</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span> <span class=\"s\">\"./\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>然后读取配置文件,目录为上一句所得到的 <code class=\"language-plaintext highlighter-rouge\">dir</code>,文件名(不含扩展名)为 <code class=\"language-plaintext highlighter-rouge\">application.basename</code> 内置配置属性值,其默认值为 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>,然后加上点和扩展名 <code class=\"language-plaintext highlighter-rouge\">.ini</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">loadConfiguration</span><span class=\"p\">(</span><span class=\"n\">dir</span>\n <span class=\"o\">+</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.baseName\"</span><span class=\"p\">,</span> <span class=\"s\">\"CumulusServer\"</span><span class=\"p\">)</span>\n <span class=\"o\">+</span> <span class=\"s\">\".ini\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这样就加载完配置了。然后查看当前的进程是从命令行运行的(命令行是交互的,所以是 interactive),还是以 daemon 方式运行的,这个函数是ServerApplication的一个成员函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_isInteractive</span> <span class=\"o\">=</span> <span class=\"n\">isInteractive</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>然后获取表示日志文件所在目录的字符串,其中 <code class=\"language-plaintext highlighter-rouge\">logs.directory</code> 是外置配置属性(配置文件中),其默认值为上面获取到的可执行文件路径(一般为当前路径)与 <code class=\"language-plaintext highlighter-rouge\">logs</code> 的组合,即一般为当前目录下的 <code class=\"language-plaintext highlighter-rouge\">logs</code> 目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">string</span> <span class=\"nf\">logDir</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"logs.directory\"</span><span class=\"p\">,</span> <span class=\"n\">dir</span> <span class=\"o\">+</span> <span class=\"s\">\"logs\"</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>创建日志文件目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span><span class=\"p\">(</span><span class=\"n\">logDir</span><span class=\"p\">).</span><span class=\"n\">createDirectory</span><span class=\"p\">();</span>\n\n</code></pre></div></div>\n\n<p>日志文件绝对路径,<code class=\"language-plaintext highlighter-rouge\">logs</code> 为默认的日志文件名(不含扩展名的部分),扩展名用0:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logPath</span> <span class=\"o\">=</span> <span class=\"n\">logDir</span> <span class=\"o\">+</span> <span class=\"s\">\"/\"</span> <span class=\"o\">+</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"logs.name\"</span><span class=\"p\">,</span> <span class=\"s\">\"log\"</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"s\">\".\"</span><span class=\"p\">;</span>\n <span class=\"n\">_pLogFile</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nf\">File</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"s\">\"0\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>用日志流打开日志文件(方式为追加写入):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">open</span><span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">path</span><span class=\"p\">(),</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">in</span> <span class=\"o\">|</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">ate</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Logs</code> 是一个方法类(其中的 <code class=\"language-plaintext highlighter-rouge\">public</code> 函数都是静态的),<code class=\"language-plaintext highlighter-rouge\">SetLogger</code> 的作用就是将Logs中的似有静态成员设置为某个 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Logger</code> 对象(由于 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 继承了 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Logger</code>)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetLogger</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4maincpp-中的-cumulusserver-的-main-成员函数\">4、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 成员函数</h4>\n\n<p>OpenRTMFP/Cumulus 是一个基于 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> 的服务端应用(准确的说是基于 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::ServerApplication</code> 的服务端应用)。如果没有特殊的启动要求,可以调用 <code class=\"language-plaintext highlighter-rouge\">Poco/Application.h</code> 中定义的宏 <code class=\"language-plaintext highlighter-rouge\">POCO_APP_MAIN</code> 来完成初始化、日志和启动(该宏会根据不同的平台启用不同的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数)。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">run()</code> 函数在调用完 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数后,会调用 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,该 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的定义在 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">>&</span> <span class=\"n\">args</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>首先看是否是要求帮助信息,<code class=\"language-plaintext highlighter-rouge\">displayHelp</code> 是借助 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::HelpFormatter</code> 实现的,<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的构造函数会在调用时将 <code class=\"language-plaintext highlighter-rouge\">_helpRequested</code> 设置为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_helpRequested</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">displayHelp</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是,则进入启动状态,首先创建一个 <code class=\"language-plaintext highlighter-rouge\">RTMFPServerParams</code> 对象 <code class=\"language-plaintext highlighter-rouge\">params</code>,用来存储 OpenRTMFP/CumulusServer 的基本配置信息。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">RTMFPServerParams</span> <span class=\"n\">params</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">params</code> 存储 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的端口号和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的端口号:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"port\"</span><span class=\"p\">,</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span><span class=\"p\">);</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">edgesPort</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"edges.port\"</span><span class=\"p\">,</span>\n <span class=\"n\">RTMFP_DEFAULT_PORT</span><span class=\"o\">+</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getBool</span><span class=\"p\">(</span><span class=\"s\">\"edges.activated\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">edgesPort</span><span class=\"o\">==</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"edges.port must have a positive value if \\\n edges.activated is true. Server edges is \\\n disactivated.\"</span><span class=\"p\">);</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesPort</span><span class=\"o\">=</span><span class=\"n\">edgesPort</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">_pCirrus</code> 为 <code class=\"language-plaintext highlighter-rouge\">SocketAddress</code> 的成员,是封装IP地址和端口号的对象。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span> <span class=\"o\">=</span> <span class=\"n\">_pCirrus</span><span class=\"p\">;</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">middle</span> <span class=\"o\">=</span> <span class=\"n\">_middle</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>UDB 所使用的缓冲区大小:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"udpBufferSize\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span>\n <span class=\"s\">\"keepAliveServer\"</span><span class=\"p\">,</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"keepAlivePeer\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>失败前 CumulusEdge 的尝试次数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span>\n <span class=\"s\">\"edges.attemptsBeforeFallback\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Server</span> <span class=\"nf\">server</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span><span class=\"s\">\"./\"</span><span class=\"p\">),</span>\n <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span><span class=\"n\">config</span><span class=\"p\">());</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">waitForTerminationRequest()</code> 函数是 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中必须调用的,意为等待终止运行的操作请求。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// wait for CTRL-C or kill</span>\n <span class=\"n\">waitForTerminationRequest</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>一旦接收到终止操作的请求,就会执行下面这句,用以退出 OpenRTMFP/Cumulus 的运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Stop the server</span>\n <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">stop</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">catch</code> 一些可能产生的异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"Configuration problem : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">what</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer unknown error\"</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>OpenRTMFP/CumulusServer 停止运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"n\">Application</span><span class=\"o\">::</span><span class=\"n\">EXIT_OK</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5cumulusserver-是-serverapplication-的子类\">5、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的子类</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 对其子类有如下要求:</p>\n\n<ul>\n <li>Subsystems must be registered in the constructor.</li>\n <li>All non-trivial initializations must be made in the <code class=\"language-plaintext highlighter-rouge\">initialize()</code>` method.</li>\n <li>At the end of the <code class=\"language-plaintext highlighter-rouge\">main()</code> method, <code class=\"language-plaintext highlighter-rouge\">waitForTerminationRequest()</code> should be called.</li>\n</ul>\n\n<h4 id=\"6serverapplication-是-application-的子类\">6、<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的子类</h4>\n\n<p>Application 对其子类的要求是,如下这些成员函数必须被覆盖:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">initialize()</code> (the one-argument, protected variant):上一篇已介绍过。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">uninitialize()</code>:下面会介绍,Application 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数会在调用 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">reinitialize()</code></li>\n <li><code class=\"language-plaintext highlighter-rouge\">defineOptions()</code>:定义命令行启动选项。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">handleOption()</code>:响应相应的命令行选项。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">main()</code></li>\n</ul>\n\n<h4 id=\"7反初始化\">7、反初始化</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是继承 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的,<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是继承 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的。<code class=\"language-plaintext highlighter-rouge\">Application</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数会先调用 <code class=\"language-plaintext highlighter-rouge\">initialize()</code>,然后调用 <code class=\"language-plaintext highlighter-rouge\">main()</code>,最后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize</code>。最后这个反初始化过程,在 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 就是直接调用父类的 <code class=\"language-plaintext highlighter-rouge\">uninitialize</code> 函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">uninitialize</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">uninitialize</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"二cumulusserver-各项交互功能的源码解读\">二、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 各项交互功能的源码解读</h3>\n\n<h4 id=\"1命令行选项设定\">1、命令行选项设定</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的命令行选项有:<code class=\"language-plaintext highlighter-rouge\">log(l)</code>、<code class=\"language-plaintext highlighter-rouge\">dump(d)</code>、<code class=\"language-plaintext highlighter-rouge\">cirrus(c)</code>、<code class=\"language-plaintext highlighter-rouge\">middle(m)</code>、<code class=\"language-plaintext highlighter-rouge\">help(h)</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">defineOptions</span><span class=\"p\">(</span><span class=\"n\">OptionSet</span><span class=\"o\">&</span> <span class=\"n\">options</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">defineOptions</span><span class=\"p\">(</span><span class=\"n\">options</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>设定日志级别(0 - 8,默认是 6,表示 info 级别)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"log\"</span><span class=\"p\">,</span> <span class=\"s\">\"l\"</span><span class=\"p\">,</span> <span class=\"s\">\"Log level argument, must be beetween 0 and 8 : \\\n nothing, fatal, critic, error, warn, note, info, debug, trace. \\\n Default value is 6 (info), all logs until info level are displayed.\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">required</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">argument</span><span class=\"p\">(</span><span class=\"s\">\"level\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>其他一些选项:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"dump\"</span><span class=\"p\">,</span> <span class=\"s\">\"d\"</span><span class=\"p\">,</span> <span class=\"s\">\"Enables packet traces in logs. Optional arguments \\\n are 'middle' or 'all' respectively to displays just middle packet \\\n process or all packet process. If no argument is given, just outside \\\n packet process will be dumped.\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">,</span><span class=\"s\">\"middle|all\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"cirrus\"</span><span class=\"p\">,</span> <span class=\"s\">\"c\"</span><span class=\"p\">,</span> <span class=\"s\">\"Cirrus address to activate a 'man-in-the-middle' \\\n developer mode in bypassing flash packets to the official cirrus \\\n server of your choice, it's a instable mode to help Cumulus developers, \\\n </span><span class=\"se\">\\\"</span><span class=\"s\">p2p.rtmfp.net:10000</span><span class=\"se\">\\\"</span><span class=\"s\"> for example. By adding the 'dump' argument, \\\n you will able to display Cirrus/Flash packet exchange in your logs \\\n (see 'dump' argument).\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">,</span><span class=\"s\">\"address\"</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"middle\"</span><span class=\"p\">,</span> <span class=\"s\">\"m\"</span><span class=\"p\">,</span><span class=\"s\">\"Enables a 'man-in-the-middle' developer mode \\\n between two peers. It's a instable mode to help Cumulus developers. \\\n By adding the 'dump' argument, you will able to display Flash/Flash \\\n packet exchange in your logs (see 'dump' argument).\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>显示帮助信息的选项:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"help\"</span><span class=\"p\">,</span> <span class=\"s\">\"h\"</span><span class=\"p\">,</span> <span class=\"s\">\"Displays help information about command-line usage.\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">required</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">OptionSet</code> 是 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::OptionSet</code>,调用addOption可以向其中增加选项Option。其中required和repeatable表示:</p>\n\n<p>Sets whether the option is required (flag == true) or optional (flag == false).\nReturns true if the option can be specified more than once, or false if at most once.\n当需要显示帮助信息时,调用如下函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">displayHelp</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">HelpFormatter</span> <span class=\"n\">helpFormatter</span><span class=\"p\">(</span><span class=\"n\">options</span><span class=\"p\">());</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setCommand</span><span class=\"p\">(</span><span class=\"n\">commandName</span><span class=\"p\">());</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setUsage</span><span class=\"p\">(</span><span class=\"s\">\"OPTIONS\"</span><span class=\"p\">);</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setHeader</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer, open source RTMFP server\"</span><span class=\"p\">);</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">cout</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">setCommand()</code>: Sets the command name.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">setUsage()</code>: Sets the usage string.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">setHeader()</code>: Sets the header string.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">format()</code>: Writes the formatted help text to the given stream.</li>\n</ul>\n\n<h4 id=\"2处理命令行选项\">2、处理命令行选项</h4>\n\n<p>参数是选项名和选项值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">handleOption</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">handleOption</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果选项是帮助:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"help\"</span><span class=\"p\">)</span>\n <span class=\"n\">_helpRequested</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"cirrus\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">URI</span> <span class=\"n\">uri</span><span class=\"p\">(</span><span class=\"s\">\"rtmfp://\"</span><span class=\"o\">+</span><span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">SocketAddress</span><span class=\"p\">(</span><span class=\"n\">uri</span><span class=\"p\">.</span><span class=\"n\">getHost</span><span class=\"p\">(),</span><span class=\"n\">uri</span><span class=\"p\">.</span><span class=\"n\">getPort</span><span class=\"p\">());</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"Mode 'man in the middle' : the exchange will bypass to '%s'\"</span><span class=\"p\">,</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Mode 'man in the middle' error : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">message</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果选项是dump日志:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"dump\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">==</span> <span class=\"s\">\"all\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">ALL</span><span class=\"p\">);</span>\n <span class=\"k\">else</span> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">==</span> <span class=\"s\">\"middle\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">MIDDLE</span><span class=\"p\">);</span>\n <span class=\"k\">else</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">EXTERNAL</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果选项是middle:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"middle\"</span><span class=\"p\">)</span>\n <span class=\"n\">_middle</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果选项是log,表示设定日志级别:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"log\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetLevel</span><span class=\"p\">(</span><span class=\"n\">atoi</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">c_str</span><span class=\"p\">()));</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"6dump-logs\">6、Dump logs</h4>\n\n<p>先加一个作用域锁,然后再向日志流写数据。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">dumpHandler</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">data</span><span class=\"p\">,</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_logMutex</span><span class=\"p\">);</span>\n <span class=\"n\">cout</span><span class=\"p\">.</span><span class=\"n\">write</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">data</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">write</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">data</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">manageLogFile</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>调用 manageLogFile,主要做一些日志大小超出限制的处理。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">manageLogFile</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先判断是否超过日志文件的大小上线,LOG_SIZE是1000000字节(即约 1 MB)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">getSize</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"n\">LOG_SIZE</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"kt\">int</span> <span class=\"n\">num</span> <span class=\"o\">=</span> <span class=\"mi\">10</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>打开新日志文件:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span> <span class=\"nf\">file</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"s\">\"10\"</span><span class=\"p\">);</span>\n\n</code></pre></div></div>\n\n<p>如果该文件已经存在,则先删除:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">exists</span><span class=\"p\">())</span>\n <span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">remove</span><span class=\"p\">();</span>\n\n <span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">--</span><span class=\"n\">num</span> <span class=\"o\">>=</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">file</span> <span class=\"o\">=</span> <span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"n\">NumberFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">num</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">exists</span><span class=\"p\">())</span>\n <span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">renameTo</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"n\">NumberFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">num</span> <span class=\"o\">+</span> <span class=\"mi\">1</span><span class=\"p\">));</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">open</span><span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">path</span><span class=\"p\">(),</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">in</span> <span class=\"o\">|</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">ate</span><span class=\"p\">);</span>\n <span class=\"err\">}</span> \n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3停止运行\">3、停止运行</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 继承了 <code class=\"language-plaintext highlighter-rouge\">ApplicationKiller</code>,该类中有纯虚函数 <code class=\"language-plaintext highlighter-rouge\">kill()</code> 需要被实现,于是有:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">kill</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">terminate</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ApplicationKiller</code> 的定义在 <code class=\"language-plaintext highlighter-rouge\">ApplicationKiller.h</code> 中,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ApplicationKiller</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"p\">(){}</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">ApplicationKiller</span><span class=\"p\">(){}</span>\n \n <span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">kill</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"4载入配置\">4、载入配置</h4>\n\n<p>在initialize()函数中调用,上一篇已提到过。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">loadConfiguration</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">path</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">loadConfiguration</span><span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">);</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5处理日志\">5、处理日志</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">logHandler</span><span class=\"p\">(</span><span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">TID</span> <span class=\"n\">threadId</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">threadName</span><span class=\"p\">,</span>\n <span class=\"n\">Priority</span> <span class=\"n\">priority</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"kt\">char</span> <span class=\"o\">*</span><span class=\"n\">filePath</span><span class=\"p\">,</span>\n <span class=\"kt\">long</span> <span class=\"n\">line</span><span class=\"p\">,</span> \n <span class=\"k\">const</span> <span class=\"kt\">char</span> <span class=\"o\">*</span><span class=\"n\">text</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>作用域锁:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_logMutex</span><span class=\"p\">);</span>\n \n <span class=\"n\">Path</span> <span class=\"nf\">path</span><span class=\"p\">(</span><span class=\"n\">filePath</span><span class=\"p\">);</span>\n <span class=\"n\">string</span> <span class=\"n\">file</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getExtension</span><span class=\"p\">()</span> <span class=\"o\">==</span> <span class=\"s\">\"lua\"</span><span class=\"p\">)</span>\n <span class=\"n\">file</span> <span class=\"o\">+=</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">directory</span><span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">depth</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"s\">\"/\"</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是命令行交互模式(即不是 daemon 模式):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_isInteractive</span><span class=\"p\">)</span>\n <span class=\"n\">printf</span><span class=\"p\">(</span><span class=\"s\">\"%s %s[%ld] %s</span><span class=\"se\">\\n</span><span class=\"s\">\"</span><span class=\"p\">,</span>\n <span class=\"n\">g_logPriorities</span><span class=\"p\">[</span><span class=\"n\">priority</span> <span class=\"o\">-</span> <span class=\"mi\">1</span><span class=\"p\">],</span>\n <span class=\"p\">(</span><span class=\"n\">file</span> <span class=\"o\">+</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getBaseName</span><span class=\"p\">()).</span><span class=\"n\">c_str</span><span class=\"p\">(),</span>\n <span class=\"n\">line</span><span class=\"p\">,</span>\n <span class=\"n\">text</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>向日志流输出一句日志:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logStream</span> <span class=\"o\"><<</span> <span class=\"n\">DateTimeFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">LocalDateTime</span><span class=\"p\">(),</span><span class=\"s\">\"%d/%m %H:%M:%S.%c \"</span><span class=\"p\">)</span>\n <span class=\"o\"><<</span> <span class=\"n\">g_logPriorities</span><span class=\"p\">[</span><span class=\"n\">priority</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'\\t'</span> <span class=\"o\"><<</span> <span class=\"n\">threadName</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'('</span> <span class=\"o\"><<</span> <span class=\"n\">threadId</span> <span class=\"o\"><<</span> <span class=\"s\">\")</span><span class=\"se\">\\t</span><span class=\"s\">\"</span>\n <span class=\"o\"><<</span> <span class=\"p\">(</span><span class=\"n\">file</span> <span class=\"o\">+</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getFileName</span><span class=\"p\">())</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'['</span> <span class=\"o\"><<</span> <span class=\"n\">line</span> <span class=\"o\"><<</span> <span class=\"s\">\"] \"</span> \n <span class=\"o\"><<</span> <span class=\"n\">text</span> <span class=\"o\"><<</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">endl</span><span class=\"p\">;</span>\n \n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">flush</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>日志文件的善后处理(主要处理文件大小限制可能产生的问题):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">manageLogFile</span><span class=\"p\">();</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三maincpp-的-main-函数源码分析\">三、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数源码分析</h3>\n\n<h4 id=\"1maincpp-中的-main-函数中的-server\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中的 <code class=\"language-plaintext highlighter-rouge\">server</code></h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中真正启动的是 <code class=\"language-plaintext highlighter-rouge\">server</code>,它继承自 <code class=\"language-plaintext highlighter-rouge\">Cumulus::RTMFPServer</code>,而 <code class=\"language-plaintext highlighter-rouge\">Cumulus::RTMFPServer</code> 又继承自 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Startable</code>、<code class=\"language-plaintext highlighter-rouge\">Cumulus::Gateway</code>、<code class=\"language-plaintext highlighter-rouge\">Cumulus::Handler</code>。而 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Startable</code> 继承自 <code class=\"language-plaintext highlighter-rouge\">Poco::Runnable</code>,所以其是一个可以运行的线程。在 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusServer</code> 中,这是主线程。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span> <span class=\"nf\">server</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span> <span class=\"s\">\"./\"</span><span class=\"p\">),</span> <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span> <span class=\"n\">config</span><span class=\"p\">());</span>\n<span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer/Server.h</code> 中定义的,其构造函数的原型为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">root</span><span class=\"p\">,</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"o\">&</span> <span class=\"n\">applicationKiller</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">AbstractConfiguration</span><span class=\"o\">&</span> <span class=\"n\">configurations</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>个参数含义如下:</p>\n\n<blockquote>\n <p>The Path Root for the Server Application Killer for Termanting the Server Application Server Configuration</p>\n</blockquote>\n\n<p>距离来说,在我的 Worksapce 中:</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">root</code> 是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 构造函数的初始化列表极长:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span><span class=\"o\">::</span><span class=\"n\">Server</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">root</span><span class=\"p\">,</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"o\">&</span> <span class=\"n\">applicationKiller</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">AbstractConfiguration</span><span class=\"o\">&</span> <span class=\"n\">configurations</span><span class=\"p\">)</span> \n <span class=\"o\">:</span> <span class=\"n\">_blacklist</span><span class=\"p\">(</span><span class=\"n\">root</span> <span class=\"o\">+</span> <span class=\"s\">\"blacklist\"</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">),</span>\n <span class=\"n\">_applicationKiller</span><span class=\"p\">(</span><span class=\"n\">applicationKiller</span><span class=\"p\">),</span>\n <span class=\"n\">_hasOnRealTime</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_pService</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">luaMail</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"o\">=</span><span class=\"n\">Script</span><span class=\"o\">::</span><span class=\"n\">CreateState</span><span class=\"p\">(),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"smtp.host\"</span><span class=\"p\">,</span><span class=\"s\">\"localhost\"</span><span class=\"p\">),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"smtp.port\"</span><span class=\"p\">,</span><span class=\"n\">SMTPSession</span><span class=\"o\">::</span><span class=\"n\">SMTP_PORT</span><span class=\"p\">),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"smtp.timeout\"</span><span class=\"p\">,</span><span class=\"mi\">60</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>下面调用 <code class=\"language-plaintext highlighter-rouge\">Poco::File</code> 创建目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span><span class=\"p\">((</span><span class=\"n\">string</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">WWWPath</span> <span class=\"o\">=</span> <span class=\"n\">root</span> <span class=\"o\">+</span> <span class=\"s\">\"www\"</span><span class=\"p\">).</span><span class=\"n\">createDirectory</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>因为 <code class=\"language-plaintext highlighter-rouge\">roor</code> 是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 目录,所以 <code class=\"language-plaintext highlighter-rouge\">WWWPath</code> 就是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/www</code> 目录。然后初始化 <code class=\"language-plaintext highlighter-rouge\">GlobalTable</code>,这个 <code class=\"language-plaintext highlighter-rouge\">GlobalTable</code> 是和 Lua 有关的东东,这里暂不细说,先知道与 Lua 相关就好。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Service</span><span class=\"o\">::</span><span class=\"n\">InitGlobalTable</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>下面就涉及到了 Lua script 了:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SCRIPT_BEGIN</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">)</span>\n <span class=\"n\">SCRIPT_CREATE_PERSISTENT_OBJECT</span><span class=\"p\">(</span><span class=\"n\">Invoker</span><span class=\"p\">,</span><span class=\"n\">LUAInvoker</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span>\n <span class=\"n\">readNextConfig</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">,</span><span class=\"n\">configurations</span><span class=\"p\">,</span><span class=\"s\">\"\"</span><span class=\"p\">);</span>\n <span class=\"n\">lua_setglobal</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">,</span><span class=\"s\">\"cumulus.configs\"</span><span class=\"p\">);</span>\n <span class=\"n\">SCRIPT_END</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">SCRIPT_BEGIN</code>、<code class=\"language-plaintext highlighter-rouge\">SCRIPT_CREATE_PERSISTENT_OBJECT和SCRIPT_END</code> 都是宏,其定义在 <code class=\"language-plaintext highlighter-rouge\">Script.h</code> 文件中,如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#define SCRIPT_BEGIN(STATE) \\\n if (lua_State* __pState = STATE) { \\\n const char* __error=NULL;\n \n#define SCRIPT_CREATE_PERSISTENT_OBJECT(TYPE,LUATYPE,OBJ) \\\n Script::WritePersistentObject<TYPE,LUATYPE>(__pState,OBJ); \\\n lua_pop(__pState,1);\n \n#define SCRIPT_END }\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">SCRIPT_BEGIN和SCRIPT_END</code> 经常用到,当与 Lua 相关的操作出现时,都会以这两个宏作为开头和结尾。</p>\n\n<h4 id=\"2maincpp-中-main-函数的-serverstart\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的 <code class=\"language-plaintext highlighter-rouge\">server.start()</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">RTMFPServerParams</span><span class=\"o\">&</span> <span class=\"n\">params</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusServer</code> 正在运行,则返回并终止启动。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer server is yet running, call stop method before\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定端口号,如果端口号为 0,则返回并终止启动。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_port</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_port</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer port must have a positive value\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusEdge</code> 的端口号,如果其端口号与 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusSever</code> 端口号相同,则返回并终止启动:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_edgesPort</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesPort</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_port</span> <span class=\"o\">==</span> <span class=\"n\">_edgesPort</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer port must different than RTMFPServer edges.port\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Cirrus:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_freqManage</span> <span class=\"o\">=</span> <span class=\"mi\">2000000</span><span class=\"p\">;</span> <span class=\"c1\">// 2 sec by default</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">Target</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span><span class=\"p\">);</span>\n <span class=\"n\">_freqManage</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span> <span class=\"c1\">// no waiting, direct process in the middle case!</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer started in man-in-the-middle mode with server %s \\\n (unstable debug mode)\"</span><span class=\"p\">,</span> <span class=\"n\">_pCirrus</span><span class=\"o\">-></span><span class=\"n\">address</span><span class=\"p\">.</span><span class=\"n\">toString</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>middle:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_middle</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">middle</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_middle</span><span class=\"p\">)</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer started in man-in-the-middle mode between peers \\\n (unstable debug mode)\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>UDP Buffer:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"o\">==</span><span class=\"mi\">0</span> <span class=\"o\">?</span> \n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">getReceiveBufferSize</span><span class=\"p\">()</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"p\">;</span>\n \n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">setReceiveBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">setSendBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">setReceiveBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">setSendBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n \n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Socket buffer receving/sending size = %u/%u\"</span><span class=\"p\">,</span>\n <span class=\"n\">udpBufferSize</span><span class=\"p\">,</span>\n <span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n \n <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\"><</span> <span class=\"mi\">5</span> <span class=\"o\">?</span> <span class=\"mi\">5000</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\"><</span> <span class=\"mi\">5</span> <span class=\"o\">?</span> <span class=\"mi\">5000</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"n\">UInt8</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">edgesAttemptsBeforeFallback</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span><span class=\"p\">;</span>\n \n <span class=\"n\">setPriority</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">threadPriority</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>启动线程,进入循环运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">();</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>上句具体的源码实现为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果在运行则返回并终止启动。然后加一个局部锁。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果不得不join()到主线程中,那就join()吧</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>然后就运行这个线程吧:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_terminate</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3回顾一下整个启动流程\">3、回顾一下整个启动流程</h4>\n\n<p>到此我们先回顾一下启动过程:</p>\n\n<p>从 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的启动入口 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数开始,创建 <code class=\"language-plaintext highlighter-rouge\">Server</code> 对象并启动(调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 函数)。<code class=\"language-plaintext highlighter-rouge\">Server::start()</code> 中调用其父类(<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code>)的父类(<code class=\"language-plaintext highlighter-rouge\">Startable</code>)的方法 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code> 开启线程。\n调用 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code> 函数后,开启线城时传入的参数为 <code class=\"language-plaintext highlighter-rouge\">*this</code>,所以就会运行 <code class=\"language-plaintext highlighter-rouge\">Startable::run()</code>;</p>\n\n<h4 id=\"4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>、<code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(...)</code> 函数源码</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Startable::run()</code> 调用 <code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 函数,但这个函数被 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖,所以会运行 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>,其源码如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP server starts on %u port\"</span><span class=\"p\">,</span><span class=\"n\">_port</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果CumulusEdge:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP edges server starts on %u port\"</span><span class=\"p\">,</span><span class=\"n\">_edgesPort</span><span class=\"p\">);</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">onStart</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>运行线程:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>处理异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">what</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer unknown error\"</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果跳出了,则终止运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">onStop</span><span class=\"p\">();</span>\n \n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP server stops\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>该函数内部又会调用父类的 <code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 函数,该函数调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>它是一个纯虚函数,由 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 实现。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 会调用 <code class=\"language-plaintext highlighter-rouge\">void run(const volatile bool& terminate)</code> 方法,该方法被 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">run</span><span class=\"p\">(</span><span class=\"n\">_terminate</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"o\">!</span><span class=\"n\">_terminate</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的 <code class=\"language-plaintext highlighter-rouge\">run(const volatile bool &terminate)</code> 方法。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">...</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</title>\n \t<meta name=\"description\" content=\"RTMFP 是 Adobe 开发的基于 UDP 协议的实时传输媒体流协议,支持 P2P 传输,具有较高的实时性和安全性。它的主要应用场景是视频通信、语音通信和网络游戏。OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。Cumulus 是一个基于 OpenRTMFP 的服务器,提供 RTMFP 服务。它具有轻量级、跨平台和可扩展的特点,并且还提供了负载均衡和可扩展性解决方案。YY 语音的 Web 端音视频流媒体能力,正是基于 RTMFP 协议做的迭代优化实现的。本文是船长关于这个系列文章的第一篇。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</h2>\t\t\n\t<time datetime=\"2012-04-09T18:57:19+00:00\" class=\"by-line\">09 Apr 2012, 广州 | 作者 麦克船长 | 总计 8401 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一rtmfp是什么\" id=\"markdown-toc-一rtmfp是什么\">一、RTMFP 是什么?</a> <ul>\n <li><a href=\"#文件分享-p2p-和实时流媒体-p2p-的区别是什么\" id=\"markdown-toc-文件分享-p2p-和实时流媒体-p2p-的区别是什么\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</a></li>\n <li><a href=\"#rtmfp-和-rtmp-之间的区别是什么\" id=\"markdown-toc-rtmfp-和-rtmp-之间的区别是什么\">RTMFP 和 RTMP 之间的区别是什么?</a></li>\n <li><a href=\"#flash-player-支持-rtmfp-吗\" id=\"markdown-toc-flash-player-支持-rtmfp-吗\">Flash Player 支持 RTMFP 吗?</a></li>\n <li><a href=\"#cumulus-使用-adobe-的-cirrus-key-吗\" id=\"markdown-toc-cumulus-使用-adobe-的-cirrus-key-吗\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</a></li>\n <li><a href=\"#这个开源项目合法吗\" id=\"markdown-toc-这个开源项目合法吗\">这个开源项目合法吗?</a></li>\n </ul>\n </li>\n <li><a href=\"#二openrtmfp和cumulus\" id=\"markdown-toc-二openrtmfp和cumulus\">二、OpenRTMFP 和 Cumulus</a></li>\n <li><a href=\"#三入门介绍与部署cumulusserver\" id=\"markdown-toc-三入门介绍与部署cumulusserver\">三、入门介绍与部署 CumulusServer</a> <ul>\n <li><a href=\"#1背景介绍\" id=\"markdown-toc-1背景介绍\">1、背景介绍</a></li>\n <li><a href=\"#2准备工作\" id=\"markdown-toc-2准备工作\">2、准备工作</a></li>\n <li><a href=\"#3安装\" id=\"markdown-toc-3安装\">3、安装</a> <ul>\n <li><a href=\"#31外部依赖的安装\" id=\"markdown-toc-31外部依赖的安装\">3.1、外部依赖的安装</a></li>\n <li><a href=\"#32安装openrtmfpcumulus\" id=\"markdown-toc-32安装openrtmfpcumulus\">3.2、安装 OpenRTMFP/Cumulus</a></li>\n </ul>\n </li>\n <li><a href=\"#4配置\" id=\"markdown-toc-4配置\">4、配置</a></li>\n <li><a href=\"#5启动\" id=\"markdown-toc-5启动\">5、启动</a></li>\n <li><a href=\"#6基本使用\" id=\"markdown-toc-6基本使用\">6、基本使用</a></li>\n <li><a href=\"#7扩展cumulusserverserverapplication\" id=\"markdown-toc-7扩展cumulusserverserverapplication\">7、扩展 CumulusServer(Server Application)</a></li>\n </ul>\n </li>\n <li><a href=\"#三用lua编写helloworld应用扩展cumulusserver\" id=\"markdown-toc-三用lua编写helloworld应用扩展cumulusserver\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</a> <ul>\n <li><a href=\"#1server-side\" id=\"markdown-toc-1server-side\">1、Server-side</a> <ul>\n <li><a href=\"#11serverconfiguration\" id=\"markdown-toc-11serverconfiguration\">1.1、Server configuration</a></li>\n <li><a href=\"#12applicationfile\" id=\"markdown-toc-12applicationfile\">1.2、Application file</a></li>\n </ul>\n </li>\n <li><a href=\"#2client-side\" id=\"markdown-toc-2client-side\">2、Client-side</a></li>\n <li><a href=\"#3运行结果\" id=\"markdown-toc-3运行结果\">3、运行结果</a></li>\n <li><a href=\"#4远程测试一个免费的测试服务器\" id=\"markdown-toc-4远程测试一个免费的测试服务器\">4、远程测试:一个免费的测试服务器</a></li>\n </ul>\n </li>\n</ul>\n\n<h3 id=\"一rtmfp是什么\">一、RTMFP 是什么?</h3>\n\n<p>Real-Time Media Flow Protocol(RTMFP)是 Adobe 开发的一种基于 UDP 并支持 P2P 的实时传输媒体流。主要特点是:高传输效率(可以使用压缩和算法来优化流量从而提高传输效率)、高实时性(可以保证媒体流的实时性使得视频通信和其他实时通信更加流畅)、支持 P2P 传输(减少对服务器的依赖从而减少带宽和服务器资源消耗)、高安全性(加密媒体流从而保证其安全性)。</p>\n\n<p>RTMFP 的主要应用场景包括:视频通信(视频聊天和视频会议)、语音通信(语音聊天、电话)、网络游戏。不过 RTMFP 目前仅有 Adobe 开发的版本,所以它并不是个开源项目,而是个商业化服务。那么有没有开源版本呢?</p>\n\n<h4 id=\"文件分享-p2p-和实时流媒体-p2p-的区别是什么\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</h4>\n\n<p>RTMFP 是一个 P2P 系统,但它仅针对实时通信时直接用户到用户之间的通信而设计,不能用于多个对等方之间进行文件共享(使用分段下载)。Facebook 在其 Pipe 应用中使用此协议将大文件直接在两个用户之间传输。</p>\n\n<h4 id=\"rtmfp-和-rtmp-之间的区别是什么\">RTMFP 和 RTMP 之间的区别是什么?</h4>\n\n<p>RTMP 是实时消息协议,RTMFP 代表实时媒体流协议。RTMFP 基于用户数据报协议(UDP),而 RTMP 基于传输控制协议(TCP)。\n与 RTMP 不同,RTMFP 还支持直接从一个 Adobe Flash Player 传输数据到另一个,而无需经过服务器。</p>\n\n<h4 id=\"flash-player-支持-rtmfp-吗\">Flash Player 支持 RTMFP 吗?</h4>\n\n<p>RTMFP 是基于用户数据报协议(UDP)的,而 RTMP 是基于传输控制协议(TCP)的。与 RTMP 不同,RTMFP 还支持直接在两个 Adobe Flash Player 之间发送数据,而不经过服务器。Flash Player 10.0 仅允许一对一通信进行 P2P,但从 10.1 开始允许应用程序级别的多播。Flash Player 查找适当的分发路由(覆盖网络),并可以将其分发到通过 P2P 连接的组。</p>\n\n<h4 id=\"cumulus-使用-adobe-的-cirrus-key-吗\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</h4>\n\n<p>不!当然,这是Cumulus的主要目标:成为Cirrus GPL的替代品。唯一的限制是:你的CPU,内存和单台机器的端口数。</p>\n\n<h4 id=\"这个开源项目合法吗\">这个开源项目合法吗?</h4>\n\n<p>在美国,数字千年版权法(Digital Millennium Copyright Act)规定,逆向工程用于协议互操作性是合法的。你可以在 WikiPedia 上查看相关讨论:</p>\n\n<ul>\n <li>http://en.wikipedia.org/wiki/Real_Time_Media_Flow_Protocol</li>\n <li>http://en.wikipedia.org/wiki/Proprietary_protocol</li>\n <li>http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act</li>\n</ul>\n\n<p>当逆向工程的目的是协议互操作性时,有法律先例。在美国,数字千年版权法(Digital Millennium Copyright Act)为逆向工程软件以使其与其他软件互操作提供了安全保障。</p>\n\n<h3 id=\"二openrtmfp和cumulus\">二、OpenRTMFP 和 Cumulus</h3>\n\n<p>OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。它包含了 RTMFP 协议的实现,以及一些额外的功能,如媒体流传输、P2P 通信、脚本引擎和数据存储。</p>\n\n<p>Cumulus 是一个基于 OpenRTMFP 的服务器,是一个完整的开源且跨平台的 RTMFP 服务器,可通过脚本进行扩展。CumulusServer 根据 GPL 许可在考虑以下 4 个概念的情况下开发:速度、轻量、跨平台和可扩展。尽管尚未发布版本,但只有在 CumulusServer 经过测试和批准后才会将代码推送到 github。实际上,主要稳定的功能有:</p>\n\n<ul>\n <li>P2P rendez-vous service</li>\n <li>live streaming</li>\n <li>RPC、pull、push exchange,实际上客户端和服务器之间的所有 AMF 可能交换</li>\n <li>脚本引擎,用于创建自己的应用服务器或扩展 Cumulus 的功能</li>\n <li>可扩展性和负载均衡解决方案</li>\n</ul>\n\n<p>下面的内容是本篇 blog 的重点,包括两部分:先是 OpenRTMFP 应用的核心 CumulusServer 的入门介绍与部署,然后用 Lua 编写 HelloWorld 应用扩展 CumulusServer,我们开始吧!</p>\n\n<h3 id=\"三入门介绍与部署cumulusserver\">三、入门介绍与部署 CumulusServer</h3>\n\n<h4 id=\"1背景介绍\">1、背景介绍</h4>\n\n<p>OpenRTMFP 可以帮助你实现 Flash 的实时应用的高并发扩展,OpenRTMFP/Cumulus 是基于 GNU General Public License 的。</p>\n\n<p>POCO:POrtable COmponents,是一个强大的开源 C++ 库。其在 C++ 开发中的角色,相当于 Java Class Library、苹果的 Cocoa、.NET framework。</p>\n\n<h4 id=\"2准备工作\">2、准备工作</h4>\n\n<p>下载:</p>\n\n<table>\n <thead>\n <tr>\n <th><strong>External Dependencies</strong></th>\n <th><strong>Official Site</strong></th>\n <th><strong>Windows</strong></th>\n <th><strong>Linux/OSX</strong></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>OpenSSL</td>\n <td><a href=\"http://www.slproweb.com/products/Win32OpenSSL.html\">Official Site</a></td>\n <td><a href=\"http://www.slproweb.com/download/Win32OpenSSL_Light-1_0_1.exe\">Download</a></td>\n <td><a href=\"http://www.openssl.org/source/openssl-1.0.1.tar.gz\">Download</a></td>\n </tr>\n <tr>\n <td>Lua</td>\n <td><a href=\"http://www.lua.org/\">Official Site</a></td>\n <td><a href=\"http://luaforwindows.googlecode.com/files/LuaForWindows_v5.1.4-45.exe\">Download</a></td>\n <td><a href=\"http://www.lua.org/ftp/lua-5.1.5.tar.gz\">Download</a></td>\n </tr>\n <tr>\n <td>POCO</td>\n <td><a href=\"http://pocoproject.org/\">Official Site</a></td>\n <td><a href=\"http://downloads.sourceforge.net/project/poco/sources/poco-1.4.3/poco-1.4.3p1.zip\">Download</a></td>\n <td><a href=\"https://sourceforge.net/projects/poco/files/sources/poco-1.4.3/poco-1.4.3p1.tar.gz/download\">Download</a></td>\n </tr>\n </tbody>\n</table>\n\n<p>注意:POCO for linux 版本必须是 1.4.0 或更高,否则会引起 TCP 相关的 bug。</p>\n\n<h4 id=\"3安装\">3、安装</h4>\n\n<h5 id=\"31外部依赖的安装\">3.1、外部依赖的安装</h5>\n\n<p>Windows 下略,Linux 下基本就是:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>./configuremakesudo\n<span class=\"nv\">$ </span>make <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<h5 id=\"32安装openrtmfpcumulus\">3.2、安装 OpenRTMFP/Cumulus</h5>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">cd </span>OpenRTMFP-Cumulus/CumulusLib\n<span class=\"nv\">$ </span>make\n<span class=\"nv\">$ </span><span class=\"nb\">cd</span> ../CumulusServer\n<span class=\"nv\">$ </span>make\n</code></pre></div></div>\n\n<p>如果出现了 <code class=\"language-plaintext highlighter-rouge\">.h</code> 文件、lib 库找不到的情况,请修改 OpenRTMFP-Cumulus/CumulusLib/Makefile 或 OpenRTMFP-Cumulus/CumulusServer/Makefile。</p>\n\n<h4 id=\"4配置\">4、配置</h4>\n\n<p>通过编写 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP-Cumulus/CumulusServer/CumulusServer.ini</code> 文件来为 OpenRTMFP-Cumulus 进行个性化配置(默认是没有这个文件的),这个文件的内容形如:</p>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">;</span><span class=\"n\">CumulusServer</span><span class=\"p\">.</span><span class=\"n\">ini</span>\n<span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"mi\">1985</span>\n<span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"mi\">114688</span>\n<span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n<span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"mi\">15</span>\n<span class=\"p\">[</span><span class=\"n\">logs</span><span class=\"p\">]</span>\n<span class=\"n\">name</span><span class=\"o\">=</span><span class=\"n\">log</span>\n<span class=\"n\">directory</span><span class=\"o\">=</span><span class=\"n\">C</span><span class=\"p\">:</span><span class=\"o\">/</span><span class=\"n\">CumulusServer</span><span class=\"o\">/</span><span class=\"n\">logs</span>\n</code></pre></div></div>\n\n<p>一些字段的设置含义如下,摘自:<a href=\"https://github.com/OpenRTMFP/Cumulus/wiki/Installation\">地址</a>。</p>\n\n<ul>\n <li>公开给 Client 的端口号 <code class=\"language-plaintext highlighter-rouge\">port</code>,默认值是 1935(RTMFP 服务器的默认端口),用于 CumulusServer 监听 RTMFP 请求。</li>\n <li>UDP 缓冲区字节数 <code class=\"language-plaintext highlighter-rouge\">udpBufferSize</code>, allows to change the size in bytes of UDP reception and sending buffer. Increases this value if your operating system has a default value too lower for important loads.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">keepAliveServer</code>, time in seconds for periodically sending packets keep-alive with server, 15s by default (valid value is from 5s to 255s).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">keepAlivePeer</code>, time in seconds for periodically sending packets keep-alive between peers, 10s by default (valid value is from 5s to 255s).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.activated</code>, activate or not the edges server on the RTMFP server (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, CumulusServer stays a RTMFP server without edges ability (default value is false).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.port</code>, port for the edges server, to accept incoming new CumulusEdge instances (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, it’s the port 1936.</li>\n</ul>\n\n<blockquote>\n <p>Warning: This port will receive plain text request from edges, for this purpose it should not be made public. It’s very important for security consideration. It must be available only for CumulusEdge instances, and anything else.</p>\n</blockquote>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.attemptsBeforeFallback</code>, number of CumulusEdge attempt connections before falling back to CumulusServer (see CumulusEdge, Scalability page for more details about CumulusEdge). By default the value is 2 (in practical, 2 attempts happens after 5 sec approximately).</li>\n <li>SMTP IP 地址 <code class=\"language-plaintext highlighter-rouge\">smtp.host</code>, configure a SMTP host to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is localhost.</li>\n <li>SMTP 端口 <code class=\"language-plaintext highlighter-rouge\">smtp.port</code>, configure a SMTP port to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 25.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">smtp.timeout</code>, configure a SMTP timeout session in seconds to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 60 seconds.</li>\n <li>日志路径 <code class=\"language-plaintext highlighter-rouge\">logs.directory</code>,默认是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer/logsby</code>。</li>\n <li>日志文件名称 <code class=\"language-plaintext highlighter-rouge\">logs.name</code>,默认是<code class=\"language-plaintext highlighter-rouge\">log</code>。</li>\n</ul>\n\n<h4 id=\"5启动\">5、启动</h4>\n\n<p>Windows 下的启动方法为:</p>\n\n<pre><code class=\"language-dos\">$ CumulusServer.exe /registerService [/displayName=CumulusServer /description=\"Open Source RTMFP Server\" /startup=automatic]\n</code></pre>\n\n<p>Unix-like 下的启动方法为:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">sudo</span> ./CumulusServer <span class=\"nt\">--daemon</span> <span class=\"o\">[</span><span class=\"nt\">--pidfile</span><span class=\"o\">=</span>/var/run/CumulusServer.pid]\n</code></pre></div></div>\n\n<p>具体地,我的启动命令为:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">sudo</span> ./CumulusServer <span class=\"nt\">--daemon</span> <span class=\"nt\">--pidfile</span><span class=\"o\">=</span>./CumulusServer.pid\n</code></pre></div></div>\n\n<h4 id=\"6基本使用\">6、基本使用</h4>\n\n<p>本地 Flash client 可以通过如下语句连接:</p>\n\n<div class=\"language-as highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">$</span> <span class=\"kd\">var</span> <span class=\"nx\">nc</span><span class=\"o\">:</span><span class=\"nx\">NetConnection</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nx\">NetConnection</span><span class=\"p\">()</span><span class=\"o\">;</span><span class=\"nx\">nc</span><span class=\"p\">.</span><span class=\"nx\">connect</span><span class=\"p\">(</span><span class=\"s2\">\"rtmfp://localhost/\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>RTMFP默认是采用1935端口,如果你特别指定了其他端口,比如12345,请使用如下方式:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>nc.connect(\"rtmfp://localhost:12345/\");\n</code></pre></div></div>\n\n<h4 id=\"7扩展cumulusserverserverapplication\">7、扩展 CumulusServer(Server Application)</h4>\n\n<p>启动CumulusServer后,会在可执行文件的目录下出现一个www目录,该目录的作用,就是作为 Server Application 的默认根目录。具体的对应关系如下:</p>\n\n<blockquote>\n <p>rtmfp://host:port/ -> [CumulusServer folder]/www/main.lua (root application)</p>\n</blockquote>\n\n<blockquote>\n <p>rtmfp://host:port/myApplication -> [CumulusServer folder]/www/myApplication/main.lua</p>\n</blockquote>\n\n<blockquote>\n <p>rtmfp://host:port/Games/myGame -> [CumulusServer folder]/www/Games/myGame/main.lua</p>\n</blockquote>\n\n<p>另外要提醒的是,如果main.lua文件被修改,则不需要重启 CumulusServer,因为 Server Application 的创建是一种动态的方式。</p>\n\n<h3 id=\"三用lua编写helloworld应用扩展cumulusserver\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</h3>\n\n<p>下面的这个实例是在本地(Client 与 Server 位于同一机器上)测试的。</p>\n\n<h4 id=\"1server-side\">1、Server-side</h4>\n\n<h5 id=\"11serverconfiguration\">1.1、Server configuration</h5>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">;</span> <span class=\"n\">CumulusServer</span><span class=\"p\">.</span><span class=\"n\">ini</span>\n<span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"mi\">1935</span>\n<span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"mi\">114688</span>\n<span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n<span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"mi\">15</span>\n<span class=\"p\">[</span><span class=\"n\">logs</span><span class=\"p\">]</span><span class=\"n\">name</span> <span class=\"o\">=</span> <span class=\"n\">log</span>\n<span class=\"n\">directory</span> <span class=\"o\">=</span> <span class=\"n\">logs</span>\n</code></pre></div></div>\n\n<h5 id=\"12applicationfile\">1.2、Application file</h5>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">function</span> <span class=\"nf\">onConnection</span><span class=\"p\">(</span><span class=\"n\">client</span><span class=\"p\">,</span><span class=\"n\">response</span><span class=\"p\">,</span><span class=\"o\">...</span><span class=\"p\">)</span>\n <span class=\"k\">function</span> <span class=\"nf\">client</span><span class=\"p\">:</span><span class=\"n\">test</span><span class=\"p\">(</span><span class=\"o\">...</span><span class=\"p\">)</span>\n <span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">firstname</span> <span class=\"o\">=</span> <span class=\"n\">unpack</span><span class=\"p\">(</span><span class=\"n\">arg</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"s2\">\"Hello \"</span><span class=\"o\">..</span><span class=\"n\">firstname</span><span class=\"o\">..</span><span class=\"s2\">\" \"</span><span class=\"o\">..</span><span class=\"n\">name</span>\n <span class=\"k\">end</span>\n <span class=\"k\">end</span>\n</code></pre></div></div>\n\n<h4 id=\"2client-side\">2、Client-side</h4>\n\n<div class=\"language-java highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// CumulusClient.as</span>\n\n<span class=\"kn\">package</span> <span class=\"err\">{</span>\n <span class=\"nn\">import</span> <span class=\"n\">flash</span><span class=\"o\">.</span><span class=\"na\">display</span><span class=\"o\">.</span><span class=\"na\">Sprite</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.NetConnection</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.NetStream</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.Responder</span><span class=\"o\">;</span>\n\n <span class=\"kd\">public</span> <span class=\"kd\">class</span> <span class=\"nc\">CumulusClient</span> <span class=\"kd\">extends</span> <span class=\"nc\">Sprite</span> <span class=\"o\">{</span>\n <span class=\"kd\">private</span> <span class=\"kt\">var</span> <span class=\"nl\">nc:</span><span class=\"nc\">NetConnection</span> <span class=\"o\">=</span> <span class=\"kc\">null</span><span class=\"o\">;</span>\n \t<span class=\"kd\">private</span> <span class=\"kt\">var</span> <span class=\"nl\">ns:</span><span class=\"nc\">NetStream</span> <span class=\"o\">=</span> <span class=\"kc\">null</span><span class=\"o\">;</span>\n \t\n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">CumulusClient</span><span class=\"o\">()</span> <span class=\"o\">{</span>\n <span class=\"n\">nc</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nc\">NetConnection</span><span class=\"o\">();</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">connect</span><span class=\"o\">(</span><span class=\"s\">\"rtmfp://localhost\"</span><span class=\"o\">);</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">client</span> <span class=\"o\">=</span> <span class=\"k\">this</span><span class=\"o\">;</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">call</span><span class=\"o\">(</span><span class=\"s\">\"test\"</span><span class=\"o\">,</span><span class=\"k\">new</span> <span class=\"nc\">Responder</span><span class=\"o\">(</span><span class=\"n\">onResult</span><span class=\"o\">,</span><span class=\"n\">onStatus</span><span class=\"o\">),</span> <span class=\"s\">\"OpenRTMFP/Cumulus\"</span><span class=\"o\">,</span> <span class=\"s\">\"World\"</span><span class=\"o\">)</span>\n <span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">close</span><span class=\"o\">():</span><span class=\"kt\">void</span> <span class=\"o\">{</span> \n\t\t\t<span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">close</span><span class=\"o\">();</span>\n \t<span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">onStatus</span><span class=\"o\">(</span><span class=\"nl\">status:</span><span class=\"nc\">Object</span><span class=\"o\">):</span><span class=\"kt\">void</span> <span class=\"o\">{</span>\n \t<span class=\"n\">trace</span><span class=\"o\">(</span><span class=\"n\">status</span><span class=\"o\">.</span><span class=\"na\">description</span><span class=\"o\">)</span>\n\t <span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">onResult</span><span class=\"o\">(</span><span class=\"nl\">response:</span><span class=\"nc\">Object</span><span class=\"o\">):</span><span class=\"kt\">void</span> <span class=\"o\">{</span>\n \t<span class=\"n\">trace</span><span class=\"o\">(</span><span class=\"n\">response</span><span class=\"o\">)</span> <span class=\"c1\">// expected to display \"Hello World OpenRTMFP/Cumulus\" </span>\n\t <span class=\"o\">}</span> \n\t<span class=\"o\">}</span>\n<span class=\"o\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3运行结果\">3、运行结果</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Hello World OpenRTMFP/Cumulus\n[SWF] CumulusClient.swf - 解压缩后为 1,776 个字节\n[卸装 SWF] CumulusClient.swf\n</code></pre></div></div>\n\n<h4 id=\"4远程测试一个免费的测试服务器\">4、远程测试:一个免费的测试服务器</h4>\n\n<p>获取 Developer Key 的地址:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>http://108.59.252.39:8080/CumulusServer/index.jsp\n</code></pre></div></div>\n\n<p>服务器配置信息:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Server: amd64 OS: Linux 2.6.18-028stab095.1\nServer IP: 108.59.252.39\nOpenRTMFP as of: 22.Feb.2012\n</code></pre></div></div>\n\n<p>编写服务器段应用地址:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>http://108.59.252.39:8080/CumulusServer/manage_ssls.jsp\n</code></pre></div></div>\n\n<p>快去试试吧 :)</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"思考":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>不要船开远了,就忘了为什么启航</title>\n \t<meta name=\"description\" content=\"2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>不要船开远了,就忘了为什么启航</h2>\t\t\n\t<time datetime=\"2022-08-11T15:53:57+00:00\" class=\"by-line\">11 Aug 2022, 杭州 | 作者 麦克船长 | 总计 3223 字</time>\n\t<div class=\"content\">\n\t\t<h3 id=\"写在前面\">写在前面</h3>\n<p>偶然翻到 2020.06.11 刚来到阿里时写的一篇内容(我是 2020 年的 6 月 4 日我入职阿里巴巴集团),是有关于来阿里的期待、对这家公司的一些粗浅初步的理解。此时再翻来看看,最大的感触就是,提醒自己勿忘初心。</p>\n\n<p>在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:</p>\n\n<ul>\n <li>很多人是带着梦想来阿里的,那么我的梦想是什么呢?</li>\n <li>最喜欢新六脉的哪句话?为什么?</li>\n <li>关于阿里企业价值观:为什么要接受这套价值观?</li>\n <li>价值观的本质意义(极度务实视角)是什么?</li>\n <li>Landing 的 SOP</li>\n <li>问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-1.png\" alt=\"image\" /></p>\n\n<p>注:2020 年平安夜 · 百年湖畔 87 期合影</p>\n\n<h3 id=\"很多人是带着梦想来阿里的那么我的梦想是什么呢\">很多人是带着梦想来阿里的,那么我的梦想是什么呢?</h3>\n\n<p>Christensen 在《创新者的窘境》中提到:每一次技术更迭,都需要破坏性创新,而破坏性创新在前一次技术更迭的胜出者内部是很难生长出来的。阿里诞生以来,不断地创造第二增长曲线:阿里巴巴、淘宝、支付宝、天猫、阿里云、钉钉 …… 这让我非常好奇。其中很多产品穿越多个时间周期,期间不断创造内生二次曲线。</p>\n\n<p>但是阿里也一样错失了很多,微信、美团、拼多多、抖/快…… 等等很多产品诞生在了其他公司,还有某些产品在不断的科技更迭中自身生长出了第二曲线。</p>\n\n<p>因此我来阿里的梦想也非常明确:<strong>参与或创造一次(甚至多次)第二曲线,可以是新产品,也可以是原有产品内生的。在这个过程中获得个人成长、个人价值。</strong></p>\n\n<p>一直以来,我有三个最想实现或得到的东西:LOVE、CREATION、FREEDOM。随着生活与工作的前行,对这三者的理解,在不断加深。在这个问题里,我想应该是讨论”CREATION”。</p>\n\n<p>CREATION 上,我的梦想的范式,大概是从自己中学时代就确立了,在某一次人类社会变革浪潮中,扮演有一定权重的角色。这里面有几个变量:<strong>什么领域(F)的变革;什么规模(S)的变革;多大的权重(W);什么角色(R)。</strong></p>\n\n<p>F 这个变量,我在中学及大学时代逐渐明确,是以相对普适的产品形式输出结果并对社会变革产生积极作用。后来越来越明确为科技与商业结合的领域。</p>\n\n<p>S、W 这两个变量,自然是越大越好。因此我会希望能够构建尽可能大的机会,或者参与到尽可能大的机会中。R 希望是有强烈 Ownership 的身份。</p>\n\n<p>因此过去几年我选择了创业。创业就像冲浪,你抓住一次浪并完成漂亮的动作,就是一次不算失败的创业。但是如果一个浪没抓住,你去追它是没意义的,而应该等待下一个浪。我认为在未来 5~10 年内难以出现规模能大到令我足够兴奋的科技浪潮。大浪潮中属于创业者的大机会很多,而中小浪潮的大机会基本只属于大平台,那么为了在壮年期做获得我的 CREATION,我选择了加入阿里这样的大平台。</p>\n\n<p>在最后做决定以及初来阿里的那个人生转折点,作为老阿里人的曲洋老师对我说的一句话,深深地鼓励了我,他说:”带着创业气质,把这里当你的舞台折腾!”</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-2.png\" alt=\"image\" /></p>\n\n<p>注:2020 年双十一 · 淘宝 KO</p>\n\n<h3 id=\"最喜欢新六脉的哪句话为什么\">最喜欢新六脉的哪句话?为什么?</h3>\n\n<p>最喜欢的是“因为信任所以简单”。</p>\n\n<p>我一直认为人最重要的两个元特质是”真实”和“谦逊”,由”真实”可以塑造自我(对内)、构建信任(对外),后者可以带来清晰的边界,继而实现人与人之间高效的互动(这种互动包括各种人际关系在内,如婚姻、合伙、共事、合作等等)。</p>\n\n<p><strong>事物(虚实皆可)呈现在人的认知中,会得到三方面的投影:facts、opinion、feeling。如果我们足够真实,当我们需要把这三方面呈现给他人时,双方就能顺畅建立信任。信任的结果,就对应到这三方面:彼此之间建立共识(facts)、求同存异(opinion)、尊重感受(feeling),这就是”简单”。</strong></p>\n\n<p>另外一句是鼓励自己勇于绽放的一条:「此次此刻,非我莫属」。</p>\n\n<p>激情、自信、积极…… 通常行为统一表现为“勇于绽放自己”,绽放有表达(语言)与投身(行为)两种表现形式。更进一步推进就是”此次此刻,非我莫属”的阿里价值观。</p>\n\n<p>低调、稳重、谦逊,其实与“此次此刻,非我莫属“,并不矛盾。这点是我来到阿里后,发现自己在过去这些年的创业中已经不知不觉改变了,从 Introvert 逐渐变成了 Extrovert 的人,而且从曾经 social 中消耗能量,逐渐变为我现在可以感知到获得能量。这种变化,是我最近来阿里才确认发生的,此前因为自己创业者的身份没有察觉这种变化的发生。</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-3.png\" alt=\"image\" /></p>\n\n<p>注:2021 年秋 · 径山之行</p>\n\n<h3 id=\"关于阿里企业价值观为什么要接受这套价值观\">关于阿里企业价值观:为什么要接受这套价值观?</h3>\n\n<p>马老师和老逍都提到这个:我们是寻找同路人,而不是教育别人。这其实非常明晰地解释了为什么阿里要构建一个毛细血管网络一样的政委体系。基于这种用人理念,政委体系不敢说是最优解,但一定是优解(而且是否有更优解的论证没有意义)。</p>\n\n<p>对于个人,我的理解是要做两件事:<strong>1)构建自己的价值观体系(初始化);2)寻找价值观契合的公司(做匹配)。这两点里,没有任何地方提到”你要改变价值观为了契合你所在的公司”。</strong></p>\n\n<p>而企业价值观呢,其实可以分两部分看待:普世价值观、独特价值观。前者因为是普世的,所以到了哪个公司这种价值观都对,这点老逍也提了,比如“客户第一”。后者是个性化的,但不存在孰高孰低,就像一个人内向还是外向,你不能说哪个是错的。</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-4.png\" alt=\"image\" /></p>\n\n<p>注:2021 年双十一 · 天天特卖团队</p>\n\n<h3 id=\"价值观的本质意义极度务实视角是什么\">价值观的本质意义(极度务实视角)是什么?</h3>\n\n<p><strong>在充分考虑价值观适配使命、愿景基础上,价值观本身的意义,在和风细雨时(即企业价值观与其他价值判断相 match 时),是看不到的。但在暴风骤雨时(即企业价值观与其他价值判断相冲突时),就能显示其实实在在的作用了。</strong>我认为包括三类,前两个是阿里整体视角,第三个是阿里内部:</p>\n\n<ul>\n <li>经济体内,阿里与其他生态位的冲突或损益关系,如曾经的美蘑口一役。</li>\n <li>经济体内,其他的生态位之间的冲突或损益关系,如曾经的十月围城。</li>\n <li>阿里人的行为价值判断,如最近的钉钉代考事件、过往的各类廉政事件。</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-5.png\" alt=\"image\" /></p>\n\n<p>注:2021 年冬 · 淘宝天猫合并前合影</p>\n\n<h3 id=\"landing的sop\">Landing 的 SOP</h3>\n\n<p>大家都说 landing 充满挑战,马老师其实给出了 landing 的 SOP 三部曲:<strong>一起打过仗、一起创过新、一起度过难。三个经历都 close 才算 smooth landing。</strong></p>\n\n<p>集团人才策略层面、HR 实操层面、Leader 层面、,对于新人 landing 能做到什么程度的保障,其实每个新人感受到的不尽相同:</p>\n\n<ul>\n <li>集团层面,始终是在构建更好的新人 landing 环境的,这符合自身价值,这能打下很好的底子。</li>\n <li>实操层面,包括面试阶段对候选人的价值观判断、预期管理,面试及入职后公司文化及人才体系的事实呈现、内化吸收和长期解惑。</li>\n <li>Leader 层面,这是新人体感最强烈的部分,也是最重要的部分。尽管拥抱变化,但首先 Leader 需要给出尽可能最全面的考虑,其次是对候选人的预期管理。好的 Leader 会给候选人提供合理的着陆点、多个降落伞、缓冲垫,完成 smooth landing。</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-6.png\" alt=\"image\" /></p>\n\n<p>注:2021 年夏 · 出差厦漳泉</p>\n\n<h3 id=\"问问自己来到阿里如果初期我可能需要做一点改变那会是什么\">问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</h3>\n\n<p>曾经个人的激情与动力,常来自于“增长”。常说<strong>高增长掩盖一切</strong>,所以未来在阿里如果不能如创业般快速获得反馈得到积极结果,并且大平台中必然要接受大量关联方共同参与项目而导致的效率降低,因此我要逐渐改变自己,重新适应这种环境下的激情与动力获得方式。</p>\n\n<p>另一方面,作为创业公司的负责人,工作中鲜有因为内部原因而无法推进的事情,但是扮演肩部或腰部角色时,需要接受头部决策的一定程度不可控,这是我需要作出的适应与改变。关于这一点,我在几个月前就已经在做预期管理和心态调整,我认为以创业者的强适应性,这可能并不会是问题,但是我习惯于保持谨慎的乐观来面对自己。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</title>\n \t<meta name=\"description\" content=\"阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</h2>\t\t\n\t<time datetime=\"2021-11-11T19:59:43+00:00\" class=\"by-line\">11 Nov 2021, 杭州 | 作者 麦克船长 | 总计 917 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2021-11-11-captain-tttm-1.jpg\" alt=\"imagee\" /></p>\n\n<h3 id=\"天天特卖团队理念\">天天特卖团队理念</h3>\n\n<h4 id=\"特卖合伙人\">特卖合伙人</h4>\n\n<p>以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。特卖核心管理团队每 Q 会进行一次班子建设通晒。</p>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-9.jpg\" alt=\"imagee\" /></p>\n\n<h4 id=\"如何理解协作\">如何理解协作?</h4>\n\n<p>从长时间线来看,我们是为了不断积累信用,像一张信用卡一样,不断获得别人愿意支持我们的更大额度。不要事情结果还可以,而我们却没有积累到信用。互联网本质也是现代工业。而现代工业,一是社会分工,二是社会协作。想取得现代工业项目的结果,就要有更大的人才包容度、环境包容度。工作的结果就是在妥协与博弈中取得的,这是和光同尘的本质,也是现代工业复杂系统拿到结果的本质。只有这样我们才能让越来越多的人追随我们一起 do something,这种追随不一定只有上下级才是,而是愿意并且相信和我们能到达更远的地方。这背后的信用,要我们一步一个脚印地去积累,对他人给予的信任要保持敬畏、如履薄冰、懂得感恩。对每一段阶段性或长或短结束的合作,都要表达感谢。</p>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-8.jpg\" alt=\"imagee\" /></p>\n\n<h4 id=\"如何看待同学的优势及短板\">如何看待同学的优势及短板?</h4>\n\n<ul>\n <li>优势:讲优势有两个可能的目的,要么组织会在未来任务分配上重点考虑发挥该同学优势的事情,要告诉 TA,要激励 TA,是 TA 前行的自信来源之一。要么是对于同学也把握不准的特点,我们明确告诉 TA 这是你被我欣赏的优点。</li>\n <li>短板:什么是要讲的短板?未来一段时间,最期待你补足提升的。一旦这方面显著进步,就会向上迈进很大一步,甚至可以突破自己当下成长的瓶颈。要花多少篇幅讲?要比优势,有更大篇幅去讲。讲完就结束了么?对这个短板,一定要表达态度,也一定要对是否有方法、什么方法来补足短板要和同学沟通。</li>\n <li>无论是优势,还是短板,要说到点儿上,不要说片儿汤话。要让同学们能够引起思考、启发的。</li>\n</ul>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-10.jpg\" alt=\"imagee\" /></p>\n\n<h3 id=\"天天特卖期待你的加入\">天天特卖期待你的加入!</h3>\n\n<p>新天天特卖缘起于「手淘下沉市场战役)」,于 2021 年初上线,以「极致性价比货源、裸价直降、全网比价、买贵必赔」打造手淘极致价格敏感人群的购物阵地。目前天天特卖团队有行业运营、用户运营、数据策略、整合营销、直播运营、内容运营等岗位,有兴趣的同学可以钉钉随时找我,期待你的加入!</p>\n\n<h4 id=\"欢迎添加我的微信sinosuperman-推荐自荐--\">欢迎添加我的微信:sinosuperman 推荐、自荐 ^ ^</h4>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-11.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-2.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-3.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-4.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-5.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-6.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-7.jpg\" alt=\"imagee\" /></p>\n\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的阿里一年香(入职阿里一周年)</title>\n \t<meta name=\"description\" content=\"本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的阿里一年香(入职阿里一周年)</h2>\t\t\n\t<time datetime=\"2021-06-04T15:42:43+00:00\" class=\"by-line\">04 Jun 2021, 杭州 | 作者 麦克船长 | 总计 304 字</time>\n\t<div class=\"content\">\n\t\t<p>To 钟超</p>\n\n<p>1 周年快乐!很开心我们有这样一段共事的机会,虽开始时有些许波折,但随着进一步相处,我们很快能做到彼此欣赏、英雄相惜、默契配合,也特别感谢你对我的信任和支持,这是一切共事的基础。你强大的自驱力、脑力、对新事物的理解学习能力,都是最近几手新人里比较突出的。特别钦佩于你的执着和初性,对一件事认定后,迸发出的强大战斗力和决心。今天特卖这个新业务需要扎下根基,还真的需要一些舍我其谁的胆魄和更为犀利的突破,我也相信「新特卖」能成为你在阿里又一代表作,我希望我们的团队能为之骄傲和自豪,我们能不负公司所托,真正在下沉市场这场硬仗上有所建树,井取得令我们自己感到骄傲的突破,一起加油。</p>\n\n<p>From 麦克船长的主管</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>担任淘宝产品总负责人的双十一,是怎样的体验?</title>\n \t<meta name=\"description\" content=\"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>担任淘宝产品总负责人的双十一,是怎样的体验?</h2>\t\t\n\t<time datetime=\"2020-11-11T15:59:43+00:00\" class=\"by-line\">11 Nov 2020, 杭州 | 作者 麦克船长 | 总计 138 字</time>\n\t<div class=\"content\">\n\t\t<p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\n\n<p><img src=\"/img/src/2020-11-11-captain-double-eleven-1.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-2.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-3.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-4.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-5.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-6.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-7.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-8.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-9.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-10.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-11.jpg\" alt=\"image\" /></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>疫后怎么做餐饮品牌?三叉戟模式或成标配</title>\n \t<meta name=\"description\" content=\"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>疫后怎么做餐饮品牌?三叉戟模式或成标配</h2>\t\t\n\t<time datetime=\"2020-04-14T16:42:43+00:00\" class=\"by-line\">14 Apr 2020, 杭州 | 作者 麦克船长 | 总计 1845 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><img src=\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\n\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\n\n<ul>\n <li>外卖</li>\n <li>做饭</li>\n <li>速食</li>\n</ul>\n\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\n\n<h3 id=\"标品化外卖业务\">标品化外卖业务</h3>\n\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\n\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\n\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\n\n<ul>\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\n</ul>\n\n<h3 id=\"社区生鲜前置仓\">社区生鲜前置仓</h3>\n\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\n\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\n\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\n\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\n\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\n\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\n\n<ul>\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\n</ul>\n\n<h3 id=\"线上预包装食品\">线上预包装食品</h3>\n\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\n\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\n\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\n\n<ul>\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\n</ul>\n\n<h3 id=\"线下重构新餐饮时代到来\">线下重构,新餐饮时代到来</h3>\n\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\n\n<ul>\n <li>用「标品化外卖」覆盖外卖场景</li>\n <li>用「生鲜前置仓」覆盖做饭场景</li>\n <li>用「预包装食品」覆盖速食场景</li>\n</ul>\n\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\n\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\n\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>延迟满足,才有自由</title>\n \t<meta name=\"description\" content=\"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>延迟满足,才有自由</h2>\t\t\n\t<time datetime=\"2020-04-11T06:18:03+00:00\" class=\"by-line\">11 Apr 2020, 杭州 | 作者 麦克船长 | 总计 4478 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\n\n<h3 id=\"1儿童时期的延迟满足\">1、儿童时期的延迟满足</h3>\n\n<h4 id=\"棉花糖实验\">棉花糖实验</h4>\n\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\n\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\n\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\n\n<p>先讲两个我自己的例子吧。</p>\n\n<h4 id=\"黑加仑零食\">黑加仑零食</h4>\n\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\n\n<h4 id=\"暑假作业\">暑假作业</h4>\n\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\n\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\n\n<h4 id=\"延迟满足的背后是意志力自控力\">延迟满足的背后,是意志力/自控力</h4>\n\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\n\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\n\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\n\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\n\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\n\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\n\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\n\n<h3 id=\"2快乐理论挑战延迟满足\">2、快乐理论,挑战延迟满足</h3>\n\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\n\n<h4 id=\"烂苹果\">烂苹果</h4>\n\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\n\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\n\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\n\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\n\n<h4 id=\"快乐理论\">快乐理论</h4>\n\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\n\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\n\n<p>但这个理论角度对吗?</p>\n\n<h3 id=\"3我们应该在哪个范畴内讨论延迟满足\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\n\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\n\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\n\n<h4 id=\"仅有正反馈刺激的奶头乐\">仅有正反馈刺激的奶头乐</h4>\n\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\n\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\n\n<h4 id=\"延迟满足重在顺序而非时间\">延迟满足重在顺序,而非时间</h4>\n\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\n\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\n\n<h3 id=\"4常见的延迟满足与即时满足\">4、常见的延迟满足与即时满足</h3>\n\n<h4 id=\"初阶对手vs浅度延迟满足\">初阶对手 vs 浅度延迟满足</h4>\n\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\n\n<ul>\n <li>\n <p>睡懒觉</p>\n </li>\n <li>\n <p>过量饮食</p>\n </li>\n <li>\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\n </li>\n <li>\n <p>冲动情绪</p>\n </li>\n <li>\n <p>炫耀(粗俗说就是装B)</p>\n </li>\n</ul>\n\n<p>……</p>\n\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\n\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\n\n<h4 id=\"中阶对手vs中度延迟满足\">中阶对手 vs 中度延迟满足</h4>\n\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\n\n<ul>\n <li>\n <p>在家边看电视/视频,边学习/工作</p>\n </li>\n <li>\n <p>依赖二手知识,而怠于一手知识</p>\n </li>\n <li>\n <p>刷知乎、行业资讯 APP</p>\n </li>\n</ul>\n\n<p>……</p>\n\n<p>例子会有很多,我们仅稍微说下这三个。</p>\n\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\n\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\n\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\n\n<h4 id=\"高阶对手vs深度延迟满足\">高阶对手 vs 深度延迟满足</h4>\n\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\n\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\n\n<p>我这里就说一个影响很大的:</p>\n\n<p>* 急于行动</p>\n\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\n\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\n\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\n\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\n\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\n\n<h3 id=\"5延迟满足的驻留时长\">5、延迟满足的驻留时长</h3>\n\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\n\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\n\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\n\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\n\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\n\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\n\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\n\n<h3 id=\"后记\">后记</h3>\n\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\n\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>未来人工智能就是要:让普通人过上现在富豪们的生活</title>\n \t<meta name=\"description\" content=\"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>未来人工智能就是要:让普通人过上现在富豪们的生活</h2>\t\t\n\t<time datetime=\"2017-02-23T18:23:33+00:00\" class=\"by-line\">23 Feb 2017, 北京 | 作者 麦克船长 | 总计 627 字</time>\n\t<div class=\"content\">\n\t\t<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\n\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\n\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\n\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\n\n<h4 id=\"但是我没有钱呢\">但是我没有钱呢?</h4>\n\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\n\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\n\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\n\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\n\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\n\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\n\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>我们是应该「断舍离」还是「念念不忘,必有回响」</title>\n \t<meta name=\"description\" content=\"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>我们是应该「断舍离」还是「念念不忘,必有回响」</h2>\t\t\n\t<time datetime=\"2017-01-31T20:59:21+00:00\" class=\"by-line\">31 Jan 2017, 北京 | 作者 麦克船长 | 总计 2577 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"引子\">引子</h3>\n\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\n\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\n\n<p>「嗨,我说老朱,讲讲你啊!」</p>\n\n<p>「我就不提啦。」</p>\n\n<p>「为什么啊?说说,说说!」</p>\n\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\n\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\n\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\n\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\n\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\n\n<blockquote>\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\n</blockquote>\n\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\n\n<h3 id=\"断舍离\">断舍离</h3>\n\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\n\n<p><img src=\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\" alt=\"image\" /></p>\n\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\n\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\n\n<h3 id=\"念念不忘必有回响\">念念不忘,必有回响</h3>\n\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\n\n<p><img src=\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\" alt=\"image\" /></p>\n\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\n\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\n\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\n\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\n\n<h3 id=\"用正负反馈来判断何时何为\">用「正负反馈」来判断何时何为?</h3>\n\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\n\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\n\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\n\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\n\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\n\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\n\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\n\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\n\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\n\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\n\n<h3 id=\"后记\">后记</h3>\n\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\n\n<p>然后,我们一起等待,那声回响。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>一名出色软件工程师的技术基本功:编程与工具</title>\n \t<meta name=\"description\" content=\"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>一名出色软件工程师的技术基本功:编程与工具</h2>\t\t\n\t<time datetime=\"2012-05-15T17:06:59+00:00\" class=\"by-line\">15 May 2012, 广州 | 作者 麦克船长 | 总计 2132 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"0写在前面\">0、写在前面</h3>\n\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\n\n<h3 id=\"1编程\">1、编程</h3>\n\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\n\n<h4 id=\"如果你精通c语言\">如果你精通 C 语言</h4>\n\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\n\n<h4 id=\"如果你精通c语言-1\">如果你精通 C++ 语言</h4>\n\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\n\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\n\n<h4 id=\"精通的关键还是针对语言核心来说的\">精通的关键,还是针对语言核心来说的。</h4>\n\n<p>第一,你要对这个语言的语法特性熟稔;</p>\n\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\n\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\n\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\n\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\n\n<h4 id=\"与计算机网络的基础结构相关联的技术实现\">与计算机、网络的基础结构相关联的技术实现</h4>\n\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\n\n<h3 id=\"2工具\">2、工具</h3>\n\n<p>对于工具有三个层面:</p>\n\n<p>第一,是熟练的使用一些工具。</p>\n\n<p>第二,是能够发现提高生产力的工具。</p>\n\n<p>第三,是能够在无可用工具时自己编写工具。</p>\n\n<p>那么都有哪些最最最基本的工具呢?</p>\n\n<h4 id=\"ideintegrateddevelopmentenvironment\">IDE(Integrated Development Environment)</h4>\n\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\n\n<h4 id=\"vcsversioncontrolsystem\">VCS(Version Control System)</h4>\n\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\n\n<blockquote>\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black & white.</p>\n</blockquote>\n\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\n\n<h4 id=\"clicommandlineinterface\">CLI(Command Line Interface)</h4>\n\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\n\n<h3 id=\"3结语\">3、结语</h3>\n\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"Jekyll":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的 Jekyll 快速教程</title>\n \t<meta name=\"description\" content=\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的 Jekyll 快速教程</h2>\t\t\n\t<time datetime=\"2021-12-23T19:43:02+00:00\" class=\"by-line\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\n\n<h3 id=\"part-1基本特点\">Part 1、基本特点</h3>\n\n<h4 id=\"一基本语法\">一、基本语法</h4>\n\n<ul>\n <li>变量:用双大括号表示变量 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code></li>\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\n <li>支持循环与分支结构:比如 <code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 和 <code class=\"language-plaintext highlighter-rouge\">if-elsif-else-endif</code> :可以使用 <code class=\"language-plaintext highlighter-rouge\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\n</ul>\n\n<h4 id=\"二典型-jekyll-项目结构及重要文件介绍\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\n\n<h5 id=\"1配置文件-_configyml\">1、配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code></h5>\n\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\"language-plaintext highlighter-rouge\">encoding: utf-8</code> 这一条。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Site settings</span>\n<span class=\"na\">encoding</span><span class=\"pi\">:</span> <span class=\"s\">utf-8</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">麦克船长的技术、产品与商业博客</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\"</span>\n<span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptain.com\"</span>\n<span class=\"na\">author</span><span class=\"pi\">:</span>\n <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Your</span><span class=\"nv\"> </span><span class=\"s\">Name\"</span>\n <span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptian.com\"</span>\n</code></pre></div></div>\n\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Markdown and highlighter</span>\n<span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Plugins</span>\n<span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-paginate</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-sitemap</span>\n</code></pre></div></div>\n\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Build settings</span>\n<span class=\"na\">baseurl</span><span class=\"pi\">:</span> <span class=\"c1\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\n<span class=\"na\">source</span><span class=\"pi\">:</span> <span class=\"s\">.</span>\n<span class=\"na\">destination</span><span class=\"pi\">:</span> <span class=\"s\">./_site</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:title</span>\n<span class=\"na\">paginate</span><span class=\"pi\">:</span> <span class=\"m\">20</span>\n<span class=\"na\">paginate_path</span><span class=\"pi\">:</span> <span class=\"s\">/page:num/</span>\n<span class=\"na\">collections</span><span class=\"pi\">:</span>\n <span class=\"na\">posts</span><span class=\"pi\">:</span>\n <span class=\"na\">output</span><span class=\"pi\">:</span> <span class=\"no\">true</span>\n <span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:year/:month/:day/:title/</span>\n <span class=\"na\">directory</span><span class=\"pi\">:</span> <span class=\"s\">_posts</span>\n</code></pre></div></div>\n\n<h5 id=\"2布局文件_layouts-目录下的文件规则\">2、布局文件:<code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的文件规则</h5>\n\n<p>Jekyll 的 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\"language-plaintext highlighter-rouge\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\"language-plaintext highlighter-rouge\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\n\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\"language-plaintext highlighter-rouge\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\n\n<h5 id=\"3页面文件及其头部\">3、页面文件及其头部</h5>\n\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">page</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/categories/</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">Categories</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>每个页面文件的头部都会有layout,并与 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的某个文件对应。</p>\n\n<h3 id=\"part-2jekyll-中的全局变量\">Part 2、Jekyll 中的全局变量</h3>\n\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:包含当前页面或文章的内容。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:包含有关网站的所有标签的信息。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\n</ul>\n\n<p>这些变量可以在模板中使用,比如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><h1></span>{{ page.title }}<span class=\"nt\"></h1></span>\n<span class=\"nt\"><p></span>{{ site.description }}<span class=\"nt\"></p></span>\n<span class=\"nt\"><ul></span>\n {{ for category in site.categories %}\n <span class=\"nt\"><li></span>{{ category }}<span class=\"nt\"></li></span>\n {{ endfor %}\n<span class=\"nt\"></ul></span> \n</code></pre></div></div>\n\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">my_custom_variable</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Hello</span><span class=\"nv\"> </span><span class=\"s\">World\"</span>\n</code></pre></div></div>\n\n<p>然后就可以在模板文件中使用 <code class=\"language-plaintext highlighter-rouge\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\n\n<h4 id=\"一site变量\">一、site变量</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\n\n<h5 id=\"1sitecategories\">1、<code class=\"language-plaintext highlighter-rouge\">site.categories</code></h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\"language-plaintext highlighter-rouge\">first</code> 就是 <code class=\"language-plaintext highlighter-rouge\">category</code> 的名字,如下使用:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>\n</code></pre></div></div>\n\n<h5 id=\"2sitepages\">2、site.pages</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\"language-plaintext highlighter-rouge\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3其他常用属性\">3、其他常用属性</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site.title</code>:是网站的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.related_posts</code>:相关文章的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.data</code>:从 <code class=\"language-plaintext highlighter-rouge\">_data</code> 目录加载的数据。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.static_files</code>:静态文件的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.collections</code>:自定义集合的列表。</li>\n</ul>\n\n<h4 id=\"二page-变量\">二、<code class=\"language-plaintext highlighter-rouge\">page</code> 变量</h4>\n\n<p>在 Jekyll 中,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量属性包括:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">layout</code>:表示页面使用的布局模板的名称。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">title</code>:表示页面的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">date</code>:表示页面的发布日期。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">categories</code>:表示页面所属的分类列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:表示页面所属的标签列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\n</ul>\n\n<p>在模板中,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.属性名 }}</code> 的方式来访问 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.title }}</code>。此外,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量还有其他属性,如 <code class=\"language-plaintext highlighter-rouge\">permalink</code>、<code class=\"language-plaintext highlighter-rouge\">excerpt</code>、<code class=\"language-plaintext highlighter-rouge\">url</code> 等,可以根据需要调用。</p>\n\n<h3 id=\"part-3控制结构\">Part 3、控制结构</h3>\n\n<h4 id=\"1if-else-分支结构\">1、<code class=\"language-plaintext highlighter-rouge\">if-else</code> 分支结构</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ if tmp_var == \"type1\" %}\n{{ elsif tmp_var == \"type2\" %}\n{{ elsif tmp_var == \"type3\" %}\n{{ elsif tmp_var == \"type4\" %}\n{{ else tmp_var == \"type5\" %}\n{{ endif %}\n</code></pre></div></div>\n\n<h4 id=\"2for-endfor-循环结构\">2、<code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 循环结构</h4>\n\n<p>不带条件判断的 <code class=\"language-plaintext highlighter-rouge\">for</code> 循环如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for post in paginator.posts %}\n\t<span class=\"c\"><!-- Your other sentences --></span>\n{{ endfor %}\n</code></pre></div></div>\n\n<p>带条件循环的 <code class=\"language-plaintext highlighter-rouge\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages | where: \"dir\", \"categories\" %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3jekyll-支持的其他结构包括\">3、Jekyll 支持的其他结构包括:</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">capture</code> 用于捕获输出的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">cycle</code> 用于循环一组字符串的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">include</code> 用于包含其他文件的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">while</code> 用于在满足指定条件时执行代码的结构。</li>\n</ul>\n\n<h3 id=\"参考\">参考:</h3>\n\n<p>1、<a href=\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\n2、<a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\n \t<meta name=\"description\" content=\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\t\t\n\t<time datetime=\"2021-12-21T15:53:57+00:00\" class=\"by-line\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#写在前面\" id=\"markdown-toc-写在前面\">写在前面</a></li>\n <li><a href=\"#1github上的准备\" id=\"markdown-toc-1github上的准备\">1、GitHub 上的准备</a></li>\n <li><a href=\"#2了解ruby和jekyll\" id=\"markdown-toc-2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</a></li>\n <li><a href=\"#3了解gem\" id=\"markdown-toc-3了解gem\">3、了解 Gem</a></li>\n <li><a href=\"#4安装homebrew\" id=\"markdown-toc-4安装homebrew\">4、安装 Homebrew</a></li>\n <li><a href=\"#5用homebrew安装ruby\" id=\"markdown-toc-5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</a></li>\n <li><a href=\"#6安装jekyll和bundler\" id=\"markdown-toc-6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</a></li>\n <li><a href=\"#7使用bundle管理包依赖关系\" id=\"markdown-toc-7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</a></li>\n <li><a href=\"#8本地启动一下看看\" id=\"markdown-toc-8本地启动一下看看\">8、本地启动一下看看</a></li>\n <li><a href=\"#9用jekyll创建一个项目\" id=\"markdown-toc-9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</a></li>\n <li><a href=\"#10修改gemfile文件\" id=\"markdown-toc-10修改gemfile文件\">10、修改 Gemfile 文件</a></li>\n <li><a href=\"#11配置githubpages\" id=\"markdown-toc-11配置githubpages\">11、配置 Github Pages</a></li>\n <li><a href=\"#12配置一个jekylltheme\" id=\"markdown-toc-12配置一个jekylltheme\">12、配置一个 Jekyll Theme</a></li>\n <li><a href=\"#13设置自定义域名\" id=\"markdown-toc-13设置自定义域名\">13、设置自定义域名</a></li>\n <li><a href=\"#14用-rouge-实现代码高亮\" id=\"markdown-toc-14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</a></li>\n <li><a href=\"#15一些扩展问题\" id=\"markdown-toc-15一些扩展问题\">15、一些扩展问题</a> <ul>\n <li><a href=\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\" id=\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\n <li><a href=\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\" id=\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\n <li><a href=\"#q3如何为每篇文章添加一个目录\" id=\"markdown-toc-q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</a></li>\n <li><a href=\"#q4如何在-jekyll-中支持-katex\" id=\"markdown-toc-q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\n <li><a href=\"#在-githubio-上\" id=\"markdown-toc-在-githubio-上\">在 GitHub.io 上</a></li>\n <li><a href=\"#如果不在-githubio-上则还需要额外工作\" id=\"markdown-toc-如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\n <li><a href=\"#使用示例\" id=\"markdown-toc-使用示例\">使用示例</a></li>\n </ul>\n </li>\n <li><a href=\"#q5jekyll-中如何支持-graphviz-\" id=\"markdown-toc-q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\n <li><a href=\"#q6如何显示--或者--\" id=\"markdown-toc-q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</a></li>\n </ul>\n </li>\n <li><a href=\"#参考\" id=\"markdown-toc-参考\">参考</a></li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\n\n<ul>\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\n</ul>\n\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\n\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\n\n<h3 id=\"1github上的准备\">1、GitHub 上的准备</h3>\n\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git clone https://github.com/username/username.github.io\n</code></pre></div></div>\n\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git add <span class=\"nt\">--all</span>\n<span class=\"nv\">$ </span>git commit <span class=\"nt\">-m</span> <span class=\"s2\">\"Initial commit\"</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</h3>\n\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\n\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\n\n<h3 id=\"3了解gem\">3、了解 Gem</h3>\n\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\n\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\ngem sources -l\n</code></pre></div></div>\n\n<h3 id=\"4安装homebrew\">4、安装 Homebrew</h3>\n\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>/bin/bash <span class=\"nt\">-c</span> <span class=\"s2\">\"</span><span class=\"si\">$(</span>curl <span class=\"nt\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\"si\">)</span><span class=\"s2\">\"</span>\n</code></pre></div></div>\n\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\n\n<h3 id=\"5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</h3>\n\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>brew <span class=\"nb\">install </span>chruby ruby-install xz\n</code></pre></div></div>\n\n<p>安装 Ruby 的最新版本:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby-install ruby\n</code></pre></div></div>\n\n<p>这时候提示如下问题:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"o\">>>></span> Updating ruby versions ...\n<span class=\"o\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\"se\">\\</span>\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\n<span class=\"o\">!!!</span> Failed to download ruby versions!\n</code></pre></div></div>\n\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ echo \"185.199.111.133 raw.githubusercontent.com\" >> /etc/hosts\n</code></pre></div></div>\n\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/chruby.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/auto.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"chruby ruby-3.1.2\"</span> <span class=\"o\">>></span> ~/.zshrc <span class=\"c\"># run 'chruby' to see actual version</span>\n</code></pre></div></div>\n\n<p>再看下 Ruby 版本对不对:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby <span class=\"nt\">-v</span>\n</code></pre></div></div>\n\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\n\n<h3 id=\"6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"nb\">install </span>jekyll bundler\n</code></pre></div></div>\n\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\n\n<h3 id=\"7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</h3>\n\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">source</span> <span class=\"s1\">'https://rubygems.org'</span>\ngem <span class=\"s1\">'nokogiri'</span>\ngem <span class=\"s1\">'rack'</span>, <span class=\"s1\">'~> 2.2.4'</span>\ngem <span class=\"s1\">'rspec'</span>\ngem <span class=\"s1\">'jekyll'</span>\n</code></pre></div></div>\n\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ git add Gemfile Gemfile.lock\n</code></pre></div></div>\n\n<h3 id=\"8本地启动一下看看\">8、本地启动一下看看</h3>\n\n<p>先用 bundle 如下命令来启动:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: none\n Source: /Users/captain/Workspace/poechant.github.io\n Destination: /Users/captain/Workspace/poechant.github.io/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n done in 0.014 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\n Server address: http://127.0.0.1:4000\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\" alt=\"image\" /></p>\n\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git pull <span class=\"nt\">--no-rebase</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll new CaptainMikeBlog\n<span class=\"nv\">$ </span><span class=\"nb\">cd </span>CaptainMikeBlog\n<span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n Jekyll Feed: Generating feed for posts\n done in 0.365 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\n Server address: http://127.0.0.1:4000/\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\" alt=\"image\" /></p>\n\n<h3 id=\"10修改gemfile文件\">10、修改 Gemfile 文件</h3>\n\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"s2\">\"github-pages\"</span>, <span class=\"s2\">\"~> GITHUB-PAGES-VERSION\"</span>, group: :jekyll_plugins\n</code></pre></div></div>\n\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ bundle install\n</code></pre></div></div>\n\n<p>再本地启动服务器测试:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>得到如下提示:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\nPrepending <span class=\"sb\">`</span>bundle <span class=\"nb\">exec</span><span class=\"sb\">`</span> to your <span class=\"nb\">command </span>may solve this. <span class=\"o\">(</span>Gem::LoadError<span class=\"o\">)</span>\n</code></pre></div></div>\n\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle add webrick\n<span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\n\n<h3 id=\"11配置githubpages\">11、配置 Github Pages</h3>\n\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>baseurl: \"\"\nurl: \"http://your-username.github.io\"\n</code></pre></div></div>\n\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\n\n<h3 id=\"12配置一个jekylltheme\">12、配置一个 Jekyll Theme</h3>\n\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_includes\n_layouts\n_sass\ncss\njs\nimg\n404.markdown\nindex.html\n</code></pre></div></div>\n\n<p>不同主题会有所不同,这里只列个大概。</p>\n\n<h3 id=\"13设置自定义域名\">13、设置自定义域名</h3>\n\n<p>添加四条 A 记录,记录值如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>185.199.108.153\n185.199.109.153\n185.199.110.153\n185.199.111.153\n</code></pre></div></div>\n\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\n\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\n\n<h3 id=\"14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</h3>\n\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem install kramdom rouge\n</code></pre></div></div>\n\n<p>然后配置 _config.yml 文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>然后用 rouge 创建 syntax.css 文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>rougify style github <span class=\"o\">></span> css/syntax.css\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">_include/head.html</code> 文件中添加:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span> <span class=\"na\">href=</span><span class=\"s\">\"/css/syntax.css\"</span> <span class=\"nt\">/></span>\n</code></pre></div></div>\n\n<h3 id=\"15一些扩展问题\">15、一些扩展问题</h3>\n\n<h4 id=\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\n\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">这是一篇文章</span>\n<span class=\"na\">excerpt</span><span class=\"pi\">:</span> <span class=\"s\">这是文章的摘要</span>\n<span class=\"nn\">---</span>\n\n这是文章的正文内容\n</code></pre></div></div>\n\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><ul></span>\n {% for post in paginator.posts %}\n <span class=\"nt\"><li></span>\n <span class=\"nt\"><h2><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{{ post.url }}\"</span><span class=\"nt\">></span>{{ post.title }}<span class=\"nt\"></a></h2></span>\n <span class=\"nt\"><p></span>{{ post.excerpt }}<span class=\"nt\"></p></span>\n <span class=\"nt\"></li></span>\n {% endfor %}\n<span class=\"nt\"></ul></span>\n</code></pre></div></div>\n\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\n\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\n\n<h4 id=\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\n\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\"language-plaintext highlighter-rouge\">_layouts</code>目录下创建一个<code class=\"language-plaintext highlighter-rouge\">category.html</code>文件:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>---\nlayout: default\n---\n\n<span class=\"nt\"><div</span> <span class=\"na\">class=</span><span class=\"s\">\"container\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><br></span>\n {% if site.categories[page.category] %}\n {% for post in site.categories[page.category] %}\n <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{% if site.baseurl == \"</span><span class=\"err\">/\"</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">}}{%</span> <span class=\"na\">else</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">|</span> <span class=\"na\">prepend:</span> <span class=\"na\">site.baseurl</span> <span class=\"err\">}}{%</span> <span class=\"na\">endif</span> <span class=\"err\">%}\"</span><span class=\"nt\">></span>\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\n <span class=\"nt\"></a></span>\n {% endfor %}\n {% else %}\n <span class=\"nt\"><br></span>\n <span class=\"nt\"><p></span>No posts for this category. If you have something in mind, check <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"/write\"</span><span class=\"nt\">></span>Write For Us<span class=\"nt\"></a></span>page.<span class=\"nt\"></p></span>\n {% endif %}\n<span class=\"nt\"></div></span>\n</code></pre></div></div>\n\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\"language-plaintext highlighter-rouge\">_config.yml</code>文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">include</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"s1\">'</span><span class=\"s\">_categories'</span><span class=\"pi\">]</span>\n</code></pre></div></div>\n\n<p>在根目录创建一个<code class=\"language-plaintext highlighter-rouge\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\"language-plaintext highlighter-rouge\">ai.html</code>如下:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">category</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">人工智能</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s\">This is the description.</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/category/ai</span>\n<span class=\"na\">category</span><span class=\"pi\">:</span> <span class=\"s\">ai</span>\n<span class=\"na\">category_type</span><span class=\"pi\">:</span> <span class=\"s\">tech</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>那么之后每次创建文件时,在头部写<code class=\"language-plaintext highlighter-rouge\">category</code>一定要与这些<code class=\"language-plaintext highlighter-rouge\">categories</code>中的<code class=\"language-plaintext highlighter-rouge\">html</code>文件对应起来。</p>\n\n<h4 id=\"q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</h4>\n\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">*</span> TOC\n{:toc}\n</code></pre></div></div>\n\n<h4 id=\"q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\n\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\n\n<h5 id=\"在-githubio-上\">在 GitHub.io 上</h5>\n\n<p>先修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code>:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">math_engine</span><span class=\"pi\">:</span> <span class=\"s\">katex</span>\n</code></pre></div></div>\n\n<p>然后修改 <code class=\"language-plaintext highlighter-rouge\">_includes/head.html</code> 文件,在 <code class=\"language-plaintext highlighter-rouge\"><head></code> 与 <code class=\"language-plaintext highlighter-rouge\"></head></code> 中间:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"><!--KaTeX--></span>\n <span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span>\n <span class=\"na\">href=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script></span>\n <span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">addEventListener</span><span class=\"p\">(</span><span class=\"dl\">\"</span><span class=\"s2\">DOMContentLoaded</span><span class=\"dl\">\"</span><span class=\"p\">,</span> <span class=\"kd\">function</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"nx\">renderMathInElement</span><span class=\"p\">(</span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">body</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n <span class=\"c1\">// ...options...</span>\n <span class=\"p\">});</span>\n <span class=\"p\">});</span>\n <span class=\"nt\"></script></span>\n</code></pre></div></div>\n\n<h5 id=\"如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</h5>\n\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem <span class=\"nb\">install </span>kramdom-math-katex\n\ngem <span class=\"nb\">install </span>katex\ngem <span class=\"nb\">install </span>execjs\n\ngem <span class=\"nb\">install </span>therubyracer\ngem <span class=\"nb\">install </span>therubyrhino\ngem <span class=\"nb\">install </span>duktape\n</code></pre></div></div>\n\n<h5 id=\"使用示例\">使用示例</h5>\n\n<p>以如下方式输入输入如下内容:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}\n$$ \\sum_{i=1}^{n} a_i $$\n{% endraw %}\n</code></pre></div></div>\n\n<p>就会得到一个数学公式:</p>\n\n\\[\\sum_{i=1}^{n} a_i\\]\n\n<h4 id=\"q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\n\n<p>这要依赖 <code class=\"language-plaintext highlighter-rouge\">jekyll-graphviz-dot</code>,修改 <code class=\"language-plaintext highlighter-rouge\">Gemfile</code> 增加一句:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>group :jekyll_plugins <span class=\"k\">do\n </span>gem <span class=\"s2\">\"jekyll-graphviz-dot\"</span>\nend\n</code></pre></div></div>\n\n<p>再修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 配置文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-graphviz</span>\n</code></pre></div></div>\n\n<p>再在本地安装 graphviz,可以通过 <code class=\"language-plaintext highlighter-rouge\">conda install graphviz</code> 或者 <code class=\"language-plaintext highlighter-rouge\">brew install graphviz</code>。然后 <code class=\"language-plaintext highlighter-rouge\">bundle install</code> 再 <code class=\"language-plaintext highlighter-rouge\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\n\n<pre><code class=\"language-graphviz\">{% graph some graph title %}\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n{% endgraph %}\n</code></pre>\n\n<p>如果看到如下效果,就说明你都配置成功了:</p>\n\n<div class=\"graphviz-wrapper\">\n\n<!-- Generated by graphviz version 2.43.0 (0)\n -->\n<!-- Title: G Pages: 1 -->\n<svg role=\"img\" aria-label=\"some graph title\" width=\"89pt\" height=\"188pt\" viewBox=\"0.00 0.00 89.00 188.00\">\n<title>some graph title</title>\n<desc>\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n</desc>\n\n<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n<title>G</title>\n<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-184 85,-184 85,4 -4,4\" />\n<!-- a -->\n<g id=\"node1\" class=\"node\">\n<title>a</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-162\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">a</text>\n</g>\n<!-- b -->\n<g id=\"node2\" class=\"node\">\n<title>b</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">b</text>\n</g>\n<!-- a->b -->\n<g id=\"edge1\" class=\"edge\">\n<title>a->b</title>\n<path fill=\"none\" stroke=\"black\" d=\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\" />\n</g>\n<!-- c -->\n<g id=\"node3\" class=\"node\">\n<title>c</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-18\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">c</text>\n</g>\n<!-- b->c -->\n<g id=\"edge2\" class=\"edge\">\n<title>b->c</title>\n<path fill=\"none\" stroke=\"black\" d=\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\" />\n</g>\n<!-- c->a -->\n<g id=\"edge3\" class=\"edge\">\n<title>c->a</title>\n<path fill=\"none\" stroke=\"black\" d=\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\" />\n</g>\n</g>\n</svg>\n</div>\n\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\n\n<h4 id=\"q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</h4>\n\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 呢?方法如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}{%{% endraw %} raw %}\n{% raw %}{%{% endraw %} endraw %}\n</code></pre></div></div>\n\n<p>如上,就是用 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 把 <code class=\"language-plaintext highlighter-rouge\">{%</code> 包起来,但是 <code class=\"language-plaintext highlighter-rouge\">%}</code> 不用包。应该讲的很清楚了吧。</p>\n\n<h3 id=\"参考\">参考</h3>\n\n<ol>\n <li><a href=\"https://bundler.io\">https://bundler.io</a></li>\n <li><a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></li>\n <li><a href=\"https://zhuanlan.zhihu.com/p/87225594\">https://zhuanlan.zhihu.com/p/87225594</a></li>\n <li><a href=\"https://chat.openai.com/chat\">https://chat.openai.com/chat</a></li>\n <li><a href=\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\n <li><a href=\"https://github.com/dyutibarma/monochrome\">https://github.com/dyutibarma/monochrome</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\n <li><a href=\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\n <li><a href=\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"Github Pages":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\n \t<meta name=\"description\" content=\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\t\t\n\t<time datetime=\"2021-12-21T15:53:57+00:00\" class=\"by-line\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#写在前面\" id=\"markdown-toc-写在前面\">写在前面</a></li>\n <li><a href=\"#1github上的准备\" id=\"markdown-toc-1github上的准备\">1、GitHub 上的准备</a></li>\n <li><a href=\"#2了解ruby和jekyll\" id=\"markdown-toc-2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</a></li>\n <li><a href=\"#3了解gem\" id=\"markdown-toc-3了解gem\">3、了解 Gem</a></li>\n <li><a href=\"#4安装homebrew\" id=\"markdown-toc-4安装homebrew\">4、安装 Homebrew</a></li>\n <li><a href=\"#5用homebrew安装ruby\" id=\"markdown-toc-5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</a></li>\n <li><a href=\"#6安装jekyll和bundler\" id=\"markdown-toc-6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</a></li>\n <li><a href=\"#7使用bundle管理包依赖关系\" id=\"markdown-toc-7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</a></li>\n <li><a href=\"#8本地启动一下看看\" id=\"markdown-toc-8本地启动一下看看\">8、本地启动一下看看</a></li>\n <li><a href=\"#9用jekyll创建一个项目\" id=\"markdown-toc-9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</a></li>\n <li><a href=\"#10修改gemfile文件\" id=\"markdown-toc-10修改gemfile文件\">10、修改 Gemfile 文件</a></li>\n <li><a href=\"#11配置githubpages\" id=\"markdown-toc-11配置githubpages\">11、配置 Github Pages</a></li>\n <li><a href=\"#12配置一个jekylltheme\" id=\"markdown-toc-12配置一个jekylltheme\">12、配置一个 Jekyll Theme</a></li>\n <li><a href=\"#13设置自定义域名\" id=\"markdown-toc-13设置自定义域名\">13、设置自定义域名</a></li>\n <li><a href=\"#14用-rouge-实现代码高亮\" id=\"markdown-toc-14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</a></li>\n <li><a href=\"#15一些扩展问题\" id=\"markdown-toc-15一些扩展问题\">15、一些扩展问题</a> <ul>\n <li><a href=\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\" id=\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\n <li><a href=\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\" id=\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\n <li><a href=\"#q3如何为每篇文章添加一个目录\" id=\"markdown-toc-q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</a></li>\n <li><a href=\"#q4如何在-jekyll-中支持-katex\" id=\"markdown-toc-q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\n <li><a href=\"#在-githubio-上\" id=\"markdown-toc-在-githubio-上\">在 GitHub.io 上</a></li>\n <li><a href=\"#如果不在-githubio-上则还需要额外工作\" id=\"markdown-toc-如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\n <li><a href=\"#使用示例\" id=\"markdown-toc-使用示例\">使用示例</a></li>\n </ul>\n </li>\n <li><a href=\"#q5jekyll-中如何支持-graphviz-\" id=\"markdown-toc-q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\n <li><a href=\"#q6如何显示--或者--\" id=\"markdown-toc-q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</a></li>\n </ul>\n </li>\n <li><a href=\"#参考\" id=\"markdown-toc-参考\">参考</a></li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\n\n<ul>\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\n</ul>\n\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\n\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\n\n<h3 id=\"1github上的准备\">1、GitHub 上的准备</h3>\n\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git clone https://github.com/username/username.github.io\n</code></pre></div></div>\n\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git add <span class=\"nt\">--all</span>\n<span class=\"nv\">$ </span>git commit <span class=\"nt\">-m</span> <span class=\"s2\">\"Initial commit\"</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</h3>\n\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\n\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\n\n<h3 id=\"3了解gem\">3、了解 Gem</h3>\n\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\n\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\ngem sources -l\n</code></pre></div></div>\n\n<h3 id=\"4安装homebrew\">4、安装 Homebrew</h3>\n\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>/bin/bash <span class=\"nt\">-c</span> <span class=\"s2\">\"</span><span class=\"si\">$(</span>curl <span class=\"nt\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\"si\">)</span><span class=\"s2\">\"</span>\n</code></pre></div></div>\n\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\n\n<h3 id=\"5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</h3>\n\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>brew <span class=\"nb\">install </span>chruby ruby-install xz\n</code></pre></div></div>\n\n<p>安装 Ruby 的最新版本:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby-install ruby\n</code></pre></div></div>\n\n<p>这时候提示如下问题:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"o\">>>></span> Updating ruby versions ...\n<span class=\"o\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\"se\">\\</span>\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\n<span class=\"o\">!!!</span> Failed to download ruby versions!\n</code></pre></div></div>\n\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ echo \"185.199.111.133 raw.githubusercontent.com\" >> /etc/hosts\n</code></pre></div></div>\n\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/chruby.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/auto.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"chruby ruby-3.1.2\"</span> <span class=\"o\">>></span> ~/.zshrc <span class=\"c\"># run 'chruby' to see actual version</span>\n</code></pre></div></div>\n\n<p>再看下 Ruby 版本对不对:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby <span class=\"nt\">-v</span>\n</code></pre></div></div>\n\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\n\n<h3 id=\"6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"nb\">install </span>jekyll bundler\n</code></pre></div></div>\n\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\n\n<h3 id=\"7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</h3>\n\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">source</span> <span class=\"s1\">'https://rubygems.org'</span>\ngem <span class=\"s1\">'nokogiri'</span>\ngem <span class=\"s1\">'rack'</span>, <span class=\"s1\">'~> 2.2.4'</span>\ngem <span class=\"s1\">'rspec'</span>\ngem <span class=\"s1\">'jekyll'</span>\n</code></pre></div></div>\n\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ git add Gemfile Gemfile.lock\n</code></pre></div></div>\n\n<h3 id=\"8本地启动一下看看\">8、本地启动一下看看</h3>\n\n<p>先用 bundle 如下命令来启动:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: none\n Source: /Users/captain/Workspace/poechant.github.io\n Destination: /Users/captain/Workspace/poechant.github.io/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n done in 0.014 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\n Server address: http://127.0.0.1:4000\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\" alt=\"image\" /></p>\n\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git pull <span class=\"nt\">--no-rebase</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll new CaptainMikeBlog\n<span class=\"nv\">$ </span><span class=\"nb\">cd </span>CaptainMikeBlog\n<span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n Jekyll Feed: Generating feed for posts\n done in 0.365 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\n Server address: http://127.0.0.1:4000/\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\" alt=\"image\" /></p>\n\n<h3 id=\"10修改gemfile文件\">10、修改 Gemfile 文件</h3>\n\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"s2\">\"github-pages\"</span>, <span class=\"s2\">\"~> GITHUB-PAGES-VERSION\"</span>, group: :jekyll_plugins\n</code></pre></div></div>\n\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ bundle install\n</code></pre></div></div>\n\n<p>再本地启动服务器测试:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>得到如下提示:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\nPrepending <span class=\"sb\">`</span>bundle <span class=\"nb\">exec</span><span class=\"sb\">`</span> to your <span class=\"nb\">command </span>may solve this. <span class=\"o\">(</span>Gem::LoadError<span class=\"o\">)</span>\n</code></pre></div></div>\n\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle add webrick\n<span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\n\n<h3 id=\"11配置githubpages\">11、配置 Github Pages</h3>\n\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>baseurl: \"\"\nurl: \"http://your-username.github.io\"\n</code></pre></div></div>\n\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\n\n<h3 id=\"12配置一个jekylltheme\">12、配置一个 Jekyll Theme</h3>\n\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_includes\n_layouts\n_sass\ncss\njs\nimg\n404.markdown\nindex.html\n</code></pre></div></div>\n\n<p>不同主题会有所不同,这里只列个大概。</p>\n\n<h3 id=\"13设置自定义域名\">13、设置自定义域名</h3>\n\n<p>添加四条 A 记录,记录值如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>185.199.108.153\n185.199.109.153\n185.199.110.153\n185.199.111.153\n</code></pre></div></div>\n\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\n\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\n\n<h3 id=\"14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</h3>\n\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem install kramdom rouge\n</code></pre></div></div>\n\n<p>然后配置 _config.yml 文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>然后用 rouge 创建 syntax.css 文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>rougify style github <span class=\"o\">></span> css/syntax.css\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">_include/head.html</code> 文件中添加:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span> <span class=\"na\">href=</span><span class=\"s\">\"/css/syntax.css\"</span> <span class=\"nt\">/></span>\n</code></pre></div></div>\n\n<h3 id=\"15一些扩展问题\">15、一些扩展问题</h3>\n\n<h4 id=\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\n\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">这是一篇文章</span>\n<span class=\"na\">excerpt</span><span class=\"pi\">:</span> <span class=\"s\">这是文章的摘要</span>\n<span class=\"nn\">---</span>\n\n这是文章的正文内容\n</code></pre></div></div>\n\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><ul></span>\n {% for post in paginator.posts %}\n <span class=\"nt\"><li></span>\n <span class=\"nt\"><h2><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{{ post.url }}\"</span><span class=\"nt\">></span>{{ post.title }}<span class=\"nt\"></a></h2></span>\n <span class=\"nt\"><p></span>{{ post.excerpt }}<span class=\"nt\"></p></span>\n <span class=\"nt\"></li></span>\n {% endfor %}\n<span class=\"nt\"></ul></span>\n</code></pre></div></div>\n\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\n\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\n\n<h4 id=\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\n\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\"language-plaintext highlighter-rouge\">_layouts</code>目录下创建一个<code class=\"language-plaintext highlighter-rouge\">category.html</code>文件:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>---\nlayout: default\n---\n\n<span class=\"nt\"><div</span> <span class=\"na\">class=</span><span class=\"s\">\"container\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><br></span>\n {% if site.categories[page.category] %}\n {% for post in site.categories[page.category] %}\n <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{% if site.baseurl == \"</span><span class=\"err\">/\"</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">}}{%</span> <span class=\"na\">else</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">|</span> <span class=\"na\">prepend:</span> <span class=\"na\">site.baseurl</span> <span class=\"err\">}}{%</span> <span class=\"na\">endif</span> <span class=\"err\">%}\"</span><span class=\"nt\">></span>\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\n <span class=\"nt\"></a></span>\n {% endfor %}\n {% else %}\n <span class=\"nt\"><br></span>\n <span class=\"nt\"><p></span>No posts for this category. If you have something in mind, check <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"/write\"</span><span class=\"nt\">></span>Write For Us<span class=\"nt\"></a></span>page.<span class=\"nt\"></p></span>\n {% endif %}\n<span class=\"nt\"></div></span>\n</code></pre></div></div>\n\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\"language-plaintext highlighter-rouge\">_config.yml</code>文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">include</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"s1\">'</span><span class=\"s\">_categories'</span><span class=\"pi\">]</span>\n</code></pre></div></div>\n\n<p>在根目录创建一个<code class=\"language-plaintext highlighter-rouge\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\"language-plaintext highlighter-rouge\">ai.html</code>如下:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">category</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">人工智能</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s\">This is the description.</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/category/ai</span>\n<span class=\"na\">category</span><span class=\"pi\">:</span> <span class=\"s\">ai</span>\n<span class=\"na\">category_type</span><span class=\"pi\">:</span> <span class=\"s\">tech</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>那么之后每次创建文件时,在头部写<code class=\"language-plaintext highlighter-rouge\">category</code>一定要与这些<code class=\"language-plaintext highlighter-rouge\">categories</code>中的<code class=\"language-plaintext highlighter-rouge\">html</code>文件对应起来。</p>\n\n<h4 id=\"q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</h4>\n\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">*</span> TOC\n{:toc}\n</code></pre></div></div>\n\n<h4 id=\"q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\n\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\n\n<h5 id=\"在-githubio-上\">在 GitHub.io 上</h5>\n\n<p>先修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code>:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">math_engine</span><span class=\"pi\">:</span> <span class=\"s\">katex</span>\n</code></pre></div></div>\n\n<p>然后修改 <code class=\"language-plaintext highlighter-rouge\">_includes/head.html</code> 文件,在 <code class=\"language-plaintext highlighter-rouge\"><head></code> 与 <code class=\"language-plaintext highlighter-rouge\"></head></code> 中间:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"><!--KaTeX--></span>\n <span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span>\n <span class=\"na\">href=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script></span>\n <span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">addEventListener</span><span class=\"p\">(</span><span class=\"dl\">\"</span><span class=\"s2\">DOMContentLoaded</span><span class=\"dl\">\"</span><span class=\"p\">,</span> <span class=\"kd\">function</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"nx\">renderMathInElement</span><span class=\"p\">(</span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">body</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n <span class=\"c1\">// ...options...</span>\n <span class=\"p\">});</span>\n <span class=\"p\">});</span>\n <span class=\"nt\"></script></span>\n</code></pre></div></div>\n\n<h5 id=\"如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</h5>\n\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem <span class=\"nb\">install </span>kramdom-math-katex\n\ngem <span class=\"nb\">install </span>katex\ngem <span class=\"nb\">install </span>execjs\n\ngem <span class=\"nb\">install </span>therubyracer\ngem <span class=\"nb\">install </span>therubyrhino\ngem <span class=\"nb\">install </span>duktape\n</code></pre></div></div>\n\n<h5 id=\"使用示例\">使用示例</h5>\n\n<p>以如下方式输入输入如下内容:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}\n$$ \\sum_{i=1}^{n} a_i $$\n{% endraw %}\n</code></pre></div></div>\n\n<p>就会得到一个数学公式:</p>\n\n\\[\\sum_{i=1}^{n} a_i\\]\n\n<h4 id=\"q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\n\n<p>这要依赖 <code class=\"language-plaintext highlighter-rouge\">jekyll-graphviz-dot</code>,修改 <code class=\"language-plaintext highlighter-rouge\">Gemfile</code> 增加一句:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>group :jekyll_plugins <span class=\"k\">do\n </span>gem <span class=\"s2\">\"jekyll-graphviz-dot\"</span>\nend\n</code></pre></div></div>\n\n<p>再修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 配置文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-graphviz</span>\n</code></pre></div></div>\n\n<p>再在本地安装 graphviz,可以通过 <code class=\"language-plaintext highlighter-rouge\">conda install graphviz</code> 或者 <code class=\"language-plaintext highlighter-rouge\">brew install graphviz</code>。然后 <code class=\"language-plaintext highlighter-rouge\">bundle install</code> 再 <code class=\"language-plaintext highlighter-rouge\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\n\n<pre><code class=\"language-graphviz\">{% graph some graph title %}\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n{% endgraph %}\n</code></pre>\n\n<p>如果看到如下效果,就说明你都配置成功了:</p>\n\n<div class=\"graphviz-wrapper\">\n\n<!-- Generated by graphviz version 2.43.0 (0)\n -->\n<!-- Title: G Pages: 1 -->\n<svg role=\"img\" aria-label=\"some graph title\" width=\"89pt\" height=\"188pt\" viewBox=\"0.00 0.00 89.00 188.00\">\n<title>some graph title</title>\n<desc>\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n</desc>\n\n<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n<title>G</title>\n<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-184 85,-184 85,4 -4,4\" />\n<!-- a -->\n<g id=\"node1\" class=\"node\">\n<title>a</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-162\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">a</text>\n</g>\n<!-- b -->\n<g id=\"node2\" class=\"node\">\n<title>b</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">b</text>\n</g>\n<!-- a->b -->\n<g id=\"edge1\" class=\"edge\">\n<title>a->b</title>\n<path fill=\"none\" stroke=\"black\" d=\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\" />\n</g>\n<!-- c -->\n<g id=\"node3\" class=\"node\">\n<title>c</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-18\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">c</text>\n</g>\n<!-- b->c -->\n<g id=\"edge2\" class=\"edge\">\n<title>b->c</title>\n<path fill=\"none\" stroke=\"black\" d=\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\" />\n</g>\n<!-- c->a -->\n<g id=\"edge3\" class=\"edge\">\n<title>c->a</title>\n<path fill=\"none\" stroke=\"black\" d=\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\" />\n</g>\n</g>\n</svg>\n</div>\n\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\n\n<h4 id=\"q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</h4>\n\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 呢?方法如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}{%{% endraw %} raw %}\n{% raw %}{%{% endraw %} endraw %}\n</code></pre></div></div>\n\n<p>如上,就是用 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 把 <code class=\"language-plaintext highlighter-rouge\">{%</code> 包起来,但是 <code class=\"language-plaintext highlighter-rouge\">%}</code> 不用包。应该讲的很清楚了吧。</p>\n\n<h3 id=\"参考\">参考</h3>\n\n<ol>\n <li><a href=\"https://bundler.io\">https://bundler.io</a></li>\n <li><a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></li>\n <li><a href=\"https://zhuanlan.zhihu.com/p/87225594\">https://zhuanlan.zhihu.com/p/87225594</a></li>\n <li><a href=\"https://chat.openai.com/chat\">https://chat.openai.com/chat</a></li>\n <li><a href=\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\n <li><a href=\"https://github.com/dyutibarma/monochrome\">https://github.com/dyutibarma/monochrome</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\n <li><a href=\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\n <li><a href=\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"前端":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的 Jekyll 快速教程</title>\n \t<meta name=\"description\" content=\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的 Jekyll 快速教程</h2>\t\t\n\t<time datetime=\"2021-12-23T19:43:02+00:00\" class=\"by-line\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\n\n<h3 id=\"part-1基本特点\">Part 1、基本特点</h3>\n\n<h4 id=\"一基本语法\">一、基本语法</h4>\n\n<ul>\n <li>变量:用双大括号表示变量 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code></li>\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\n <li>支持循环与分支结构:比如 <code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 和 <code class=\"language-plaintext highlighter-rouge\">if-elsif-else-endif</code> :可以使用 <code class=\"language-plaintext highlighter-rouge\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\n</ul>\n\n<h4 id=\"二典型-jekyll-项目结构及重要文件介绍\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\n\n<h5 id=\"1配置文件-_configyml\">1、配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code></h5>\n\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\"language-plaintext highlighter-rouge\">encoding: utf-8</code> 这一条。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Site settings</span>\n<span class=\"na\">encoding</span><span class=\"pi\">:</span> <span class=\"s\">utf-8</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">麦克船长的技术、产品与商业博客</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\"</span>\n<span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptain.com\"</span>\n<span class=\"na\">author</span><span class=\"pi\">:</span>\n <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Your</span><span class=\"nv\"> </span><span class=\"s\">Name\"</span>\n <span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptian.com\"</span>\n</code></pre></div></div>\n\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Markdown and highlighter</span>\n<span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Plugins</span>\n<span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-paginate</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-sitemap</span>\n</code></pre></div></div>\n\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Build settings</span>\n<span class=\"na\">baseurl</span><span class=\"pi\">:</span> <span class=\"c1\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\n<span class=\"na\">source</span><span class=\"pi\">:</span> <span class=\"s\">.</span>\n<span class=\"na\">destination</span><span class=\"pi\">:</span> <span class=\"s\">./_site</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:title</span>\n<span class=\"na\">paginate</span><span class=\"pi\">:</span> <span class=\"m\">20</span>\n<span class=\"na\">paginate_path</span><span class=\"pi\">:</span> <span class=\"s\">/page:num/</span>\n<span class=\"na\">collections</span><span class=\"pi\">:</span>\n <span class=\"na\">posts</span><span class=\"pi\">:</span>\n <span class=\"na\">output</span><span class=\"pi\">:</span> <span class=\"no\">true</span>\n <span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:year/:month/:day/:title/</span>\n <span class=\"na\">directory</span><span class=\"pi\">:</span> <span class=\"s\">_posts</span>\n</code></pre></div></div>\n\n<h5 id=\"2布局文件_layouts-目录下的文件规则\">2、布局文件:<code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的文件规则</h5>\n\n<p>Jekyll 的 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\"language-plaintext highlighter-rouge\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\"language-plaintext highlighter-rouge\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\n\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\"language-plaintext highlighter-rouge\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\n\n<h5 id=\"3页面文件及其头部\">3、页面文件及其头部</h5>\n\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">page</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/categories/</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">Categories</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>每个页面文件的头部都会有layout,并与 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的某个文件对应。</p>\n\n<h3 id=\"part-2jekyll-中的全局变量\">Part 2、Jekyll 中的全局变量</h3>\n\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:包含当前页面或文章的内容。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:包含有关网站的所有标签的信息。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\n</ul>\n\n<p>这些变量可以在模板中使用,比如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><h1></span>{{ page.title }}<span class=\"nt\"></h1></span>\n<span class=\"nt\"><p></span>{{ site.description }}<span class=\"nt\"></p></span>\n<span class=\"nt\"><ul></span>\n {{ for category in site.categories %}\n <span class=\"nt\"><li></span>{{ category }}<span class=\"nt\"></li></span>\n {{ endfor %}\n<span class=\"nt\"></ul></span> \n</code></pre></div></div>\n\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">my_custom_variable</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Hello</span><span class=\"nv\"> </span><span class=\"s\">World\"</span>\n</code></pre></div></div>\n\n<p>然后就可以在模板文件中使用 <code class=\"language-plaintext highlighter-rouge\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\n\n<h4 id=\"一site变量\">一、site变量</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\n\n<h5 id=\"1sitecategories\">1、<code class=\"language-plaintext highlighter-rouge\">site.categories</code></h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\"language-plaintext highlighter-rouge\">first</code> 就是 <code class=\"language-plaintext highlighter-rouge\">category</code> 的名字,如下使用:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>\n</code></pre></div></div>\n\n<h5 id=\"2sitepages\">2、site.pages</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\"language-plaintext highlighter-rouge\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3其他常用属性\">3、其他常用属性</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site.title</code>:是网站的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.related_posts</code>:相关文章的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.data</code>:从 <code class=\"language-plaintext highlighter-rouge\">_data</code> 目录加载的数据。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.static_files</code>:静态文件的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.collections</code>:自定义集合的列表。</li>\n</ul>\n\n<h4 id=\"二page-变量\">二、<code class=\"language-plaintext highlighter-rouge\">page</code> 变量</h4>\n\n<p>在 Jekyll 中,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量属性包括:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">layout</code>:表示页面使用的布局模板的名称。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">title</code>:表示页面的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">date</code>:表示页面的发布日期。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">categories</code>:表示页面所属的分类列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:表示页面所属的标签列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\n</ul>\n\n<p>在模板中,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.属性名 }}</code> 的方式来访问 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.title }}</code>。此外,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量还有其他属性,如 <code class=\"language-plaintext highlighter-rouge\">permalink</code>、<code class=\"language-plaintext highlighter-rouge\">excerpt</code>、<code class=\"language-plaintext highlighter-rouge\">url</code> 等,可以根据需要调用。</p>\n\n<h3 id=\"part-3控制结构\">Part 3、控制结构</h3>\n\n<h4 id=\"1if-else-分支结构\">1、<code class=\"language-plaintext highlighter-rouge\">if-else</code> 分支结构</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ if tmp_var == \"type1\" %}\n{{ elsif tmp_var == \"type2\" %}\n{{ elsif tmp_var == \"type3\" %}\n{{ elsif tmp_var == \"type4\" %}\n{{ else tmp_var == \"type5\" %}\n{{ endif %}\n</code></pre></div></div>\n\n<h4 id=\"2for-endfor-循环结构\">2、<code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 循环结构</h4>\n\n<p>不带条件判断的 <code class=\"language-plaintext highlighter-rouge\">for</code> 循环如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for post in paginator.posts %}\n\t<span class=\"c\"><!-- Your other sentences --></span>\n{{ endfor %}\n</code></pre></div></div>\n\n<p>带条件循环的 <code class=\"language-plaintext highlighter-rouge\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages | where: \"dir\", \"categories\" %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3jekyll-支持的其他结构包括\">3、Jekyll 支持的其他结构包括:</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">capture</code> 用于捕获输出的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">cycle</code> 用于循环一组字符串的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">include</code> 用于包含其他文件的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">while</code> 用于在满足指定条件时执行代码的结构。</li>\n</ul>\n\n<h3 id=\"参考\">参考:</h3>\n\n<p>1、<a href=\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\n2、<a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\n \t<meta name=\"description\" content=\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\t\t\n\t<time datetime=\"2021-12-21T15:53:57+00:00\" class=\"by-line\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#写在前面\" id=\"markdown-toc-写在前面\">写在前面</a></li>\n <li><a href=\"#1github上的准备\" id=\"markdown-toc-1github上的准备\">1、GitHub 上的准备</a></li>\n <li><a href=\"#2了解ruby和jekyll\" id=\"markdown-toc-2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</a></li>\n <li><a href=\"#3了解gem\" id=\"markdown-toc-3了解gem\">3、了解 Gem</a></li>\n <li><a href=\"#4安装homebrew\" id=\"markdown-toc-4安装homebrew\">4、安装 Homebrew</a></li>\n <li><a href=\"#5用homebrew安装ruby\" id=\"markdown-toc-5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</a></li>\n <li><a href=\"#6安装jekyll和bundler\" id=\"markdown-toc-6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</a></li>\n <li><a href=\"#7使用bundle管理包依赖关系\" id=\"markdown-toc-7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</a></li>\n <li><a href=\"#8本地启动一下看看\" id=\"markdown-toc-8本地启动一下看看\">8、本地启动一下看看</a></li>\n <li><a href=\"#9用jekyll创建一个项目\" id=\"markdown-toc-9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</a></li>\n <li><a href=\"#10修改gemfile文件\" id=\"markdown-toc-10修改gemfile文件\">10、修改 Gemfile 文件</a></li>\n <li><a href=\"#11配置githubpages\" id=\"markdown-toc-11配置githubpages\">11、配置 Github Pages</a></li>\n <li><a href=\"#12配置一个jekylltheme\" id=\"markdown-toc-12配置一个jekylltheme\">12、配置一个 Jekyll Theme</a></li>\n <li><a href=\"#13设置自定义域名\" id=\"markdown-toc-13设置自定义域名\">13、设置自定义域名</a></li>\n <li><a href=\"#14用-rouge-实现代码高亮\" id=\"markdown-toc-14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</a></li>\n <li><a href=\"#15一些扩展问题\" id=\"markdown-toc-15一些扩展问题\">15、一些扩展问题</a> <ul>\n <li><a href=\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\" id=\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\n <li><a href=\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\" id=\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\n <li><a href=\"#q3如何为每篇文章添加一个目录\" id=\"markdown-toc-q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</a></li>\n <li><a href=\"#q4如何在-jekyll-中支持-katex\" id=\"markdown-toc-q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\n <li><a href=\"#在-githubio-上\" id=\"markdown-toc-在-githubio-上\">在 GitHub.io 上</a></li>\n <li><a href=\"#如果不在-githubio-上则还需要额外工作\" id=\"markdown-toc-如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\n <li><a href=\"#使用示例\" id=\"markdown-toc-使用示例\">使用示例</a></li>\n </ul>\n </li>\n <li><a href=\"#q5jekyll-中如何支持-graphviz-\" id=\"markdown-toc-q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\n <li><a href=\"#q6如何显示--或者--\" id=\"markdown-toc-q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</a></li>\n </ul>\n </li>\n <li><a href=\"#参考\" id=\"markdown-toc-参考\">参考</a></li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\n\n<ul>\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\n</ul>\n\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\n\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\n\n<h3 id=\"1github上的准备\">1、GitHub 上的准备</h3>\n\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git clone https://github.com/username/username.github.io\n</code></pre></div></div>\n\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git add <span class=\"nt\">--all</span>\n<span class=\"nv\">$ </span>git commit <span class=\"nt\">-m</span> <span class=\"s2\">\"Initial commit\"</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</h3>\n\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\n\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\n\n<h3 id=\"3了解gem\">3、了解 Gem</h3>\n\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\n\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\ngem sources -l\n</code></pre></div></div>\n\n<h3 id=\"4安装homebrew\">4、安装 Homebrew</h3>\n\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>/bin/bash <span class=\"nt\">-c</span> <span class=\"s2\">\"</span><span class=\"si\">$(</span>curl <span class=\"nt\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\"si\">)</span><span class=\"s2\">\"</span>\n</code></pre></div></div>\n\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\n\n<h3 id=\"5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</h3>\n\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>brew <span class=\"nb\">install </span>chruby ruby-install xz\n</code></pre></div></div>\n\n<p>安装 Ruby 的最新版本:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby-install ruby\n</code></pre></div></div>\n\n<p>这时候提示如下问题:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"o\">>>></span> Updating ruby versions ...\n<span class=\"o\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\"se\">\\</span>\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\n<span class=\"o\">!!!</span> Failed to download ruby versions!\n</code></pre></div></div>\n\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ echo \"185.199.111.133 raw.githubusercontent.com\" >> /etc/hosts\n</code></pre></div></div>\n\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/chruby.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/auto.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"chruby ruby-3.1.2\"</span> <span class=\"o\">>></span> ~/.zshrc <span class=\"c\"># run 'chruby' to see actual version</span>\n</code></pre></div></div>\n\n<p>再看下 Ruby 版本对不对:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby <span class=\"nt\">-v</span>\n</code></pre></div></div>\n\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\n\n<h3 id=\"6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"nb\">install </span>jekyll bundler\n</code></pre></div></div>\n\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\n\n<h3 id=\"7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</h3>\n\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">source</span> <span class=\"s1\">'https://rubygems.org'</span>\ngem <span class=\"s1\">'nokogiri'</span>\ngem <span class=\"s1\">'rack'</span>, <span class=\"s1\">'~> 2.2.4'</span>\ngem <span class=\"s1\">'rspec'</span>\ngem <span class=\"s1\">'jekyll'</span>\n</code></pre></div></div>\n\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ git add Gemfile Gemfile.lock\n</code></pre></div></div>\n\n<h3 id=\"8本地启动一下看看\">8、本地启动一下看看</h3>\n\n<p>先用 bundle 如下命令来启动:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: none\n Source: /Users/captain/Workspace/poechant.github.io\n Destination: /Users/captain/Workspace/poechant.github.io/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n done in 0.014 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\n Server address: http://127.0.0.1:4000\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\" alt=\"image\" /></p>\n\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git pull <span class=\"nt\">--no-rebase</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll new CaptainMikeBlog\n<span class=\"nv\">$ </span><span class=\"nb\">cd </span>CaptainMikeBlog\n<span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n Jekyll Feed: Generating feed for posts\n done in 0.365 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\n Server address: http://127.0.0.1:4000/\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\" alt=\"image\" /></p>\n\n<h3 id=\"10修改gemfile文件\">10、修改 Gemfile 文件</h3>\n\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"s2\">\"github-pages\"</span>, <span class=\"s2\">\"~> GITHUB-PAGES-VERSION\"</span>, group: :jekyll_plugins\n</code></pre></div></div>\n\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ bundle install\n</code></pre></div></div>\n\n<p>再本地启动服务器测试:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>得到如下提示:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\nPrepending <span class=\"sb\">`</span>bundle <span class=\"nb\">exec</span><span class=\"sb\">`</span> to your <span class=\"nb\">command </span>may solve this. <span class=\"o\">(</span>Gem::LoadError<span class=\"o\">)</span>\n</code></pre></div></div>\n\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle add webrick\n<span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\n\n<h3 id=\"11配置githubpages\">11、配置 Github Pages</h3>\n\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>baseurl: \"\"\nurl: \"http://your-username.github.io\"\n</code></pre></div></div>\n\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\n\n<h3 id=\"12配置一个jekylltheme\">12、配置一个 Jekyll Theme</h3>\n\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_includes\n_layouts\n_sass\ncss\njs\nimg\n404.markdown\nindex.html\n</code></pre></div></div>\n\n<p>不同主题会有所不同,这里只列个大概。</p>\n\n<h3 id=\"13设置自定义域名\">13、设置自定义域名</h3>\n\n<p>添加四条 A 记录,记录值如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>185.199.108.153\n185.199.109.153\n185.199.110.153\n185.199.111.153\n</code></pre></div></div>\n\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\n\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\n\n<h3 id=\"14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</h3>\n\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem install kramdom rouge\n</code></pre></div></div>\n\n<p>然后配置 _config.yml 文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>然后用 rouge 创建 syntax.css 文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>rougify style github <span class=\"o\">></span> css/syntax.css\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">_include/head.html</code> 文件中添加:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span> <span class=\"na\">href=</span><span class=\"s\">\"/css/syntax.css\"</span> <span class=\"nt\">/></span>\n</code></pre></div></div>\n\n<h3 id=\"15一些扩展问题\">15、一些扩展问题</h3>\n\n<h4 id=\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\n\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">这是一篇文章</span>\n<span class=\"na\">excerpt</span><span class=\"pi\">:</span> <span class=\"s\">这是文章的摘要</span>\n<span class=\"nn\">---</span>\n\n这是文章的正文内容\n</code></pre></div></div>\n\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><ul></span>\n {% for post in paginator.posts %}\n <span class=\"nt\"><li></span>\n <span class=\"nt\"><h2><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{{ post.url }}\"</span><span class=\"nt\">></span>{{ post.title }}<span class=\"nt\"></a></h2></span>\n <span class=\"nt\"><p></span>{{ post.excerpt }}<span class=\"nt\"></p></span>\n <span class=\"nt\"></li></span>\n {% endfor %}\n<span class=\"nt\"></ul></span>\n</code></pre></div></div>\n\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\n\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\n\n<h4 id=\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\n\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\"language-plaintext highlighter-rouge\">_layouts</code>目录下创建一个<code class=\"language-plaintext highlighter-rouge\">category.html</code>文件:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>---\nlayout: default\n---\n\n<span class=\"nt\"><div</span> <span class=\"na\">class=</span><span class=\"s\">\"container\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><br></span>\n {% if site.categories[page.category] %}\n {% for post in site.categories[page.category] %}\n <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{% if site.baseurl == \"</span><span class=\"err\">/\"</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">}}{%</span> <span class=\"na\">else</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">|</span> <span class=\"na\">prepend:</span> <span class=\"na\">site.baseurl</span> <span class=\"err\">}}{%</span> <span class=\"na\">endif</span> <span class=\"err\">%}\"</span><span class=\"nt\">></span>\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\n <span class=\"nt\"></a></span>\n {% endfor %}\n {% else %}\n <span class=\"nt\"><br></span>\n <span class=\"nt\"><p></span>No posts for this category. If you have something in mind, check <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"/write\"</span><span class=\"nt\">></span>Write For Us<span class=\"nt\"></a></span>page.<span class=\"nt\"></p></span>\n {% endif %}\n<span class=\"nt\"></div></span>\n</code></pre></div></div>\n\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\"language-plaintext highlighter-rouge\">_config.yml</code>文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">include</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"s1\">'</span><span class=\"s\">_categories'</span><span class=\"pi\">]</span>\n</code></pre></div></div>\n\n<p>在根目录创建一个<code class=\"language-plaintext highlighter-rouge\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\"language-plaintext highlighter-rouge\">ai.html</code>如下:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">category</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">人工智能</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s\">This is the description.</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/category/ai</span>\n<span class=\"na\">category</span><span class=\"pi\">:</span> <span class=\"s\">ai</span>\n<span class=\"na\">category_type</span><span class=\"pi\">:</span> <span class=\"s\">tech</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>那么之后每次创建文件时,在头部写<code class=\"language-plaintext highlighter-rouge\">category</code>一定要与这些<code class=\"language-plaintext highlighter-rouge\">categories</code>中的<code class=\"language-plaintext highlighter-rouge\">html</code>文件对应起来。</p>\n\n<h4 id=\"q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</h4>\n\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">*</span> TOC\n{:toc}\n</code></pre></div></div>\n\n<h4 id=\"q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\n\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\n\n<h5 id=\"在-githubio-上\">在 GitHub.io 上</h5>\n\n<p>先修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code>:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">math_engine</span><span class=\"pi\">:</span> <span class=\"s\">katex</span>\n</code></pre></div></div>\n\n<p>然后修改 <code class=\"language-plaintext highlighter-rouge\">_includes/head.html</code> 文件,在 <code class=\"language-plaintext highlighter-rouge\"><head></code> 与 <code class=\"language-plaintext highlighter-rouge\"></head></code> 中间:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"><!--KaTeX--></span>\n <span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span>\n <span class=\"na\">href=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script></span>\n <span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">addEventListener</span><span class=\"p\">(</span><span class=\"dl\">\"</span><span class=\"s2\">DOMContentLoaded</span><span class=\"dl\">\"</span><span class=\"p\">,</span> <span class=\"kd\">function</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"nx\">renderMathInElement</span><span class=\"p\">(</span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">body</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n <span class=\"c1\">// ...options...</span>\n <span class=\"p\">});</span>\n <span class=\"p\">});</span>\n <span class=\"nt\"></script></span>\n</code></pre></div></div>\n\n<h5 id=\"如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</h5>\n\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem <span class=\"nb\">install </span>kramdom-math-katex\n\ngem <span class=\"nb\">install </span>katex\ngem <span class=\"nb\">install </span>execjs\n\ngem <span class=\"nb\">install </span>therubyracer\ngem <span class=\"nb\">install </span>therubyrhino\ngem <span class=\"nb\">install </span>duktape\n</code></pre></div></div>\n\n<h5 id=\"使用示例\">使用示例</h5>\n\n<p>以如下方式输入输入如下内容:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}\n$$ \\sum_{i=1}^{n} a_i $$\n{% endraw %}\n</code></pre></div></div>\n\n<p>就会得到一个数学公式:</p>\n\n\\[\\sum_{i=1}^{n} a_i\\]\n\n<h4 id=\"q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\n\n<p>这要依赖 <code class=\"language-plaintext highlighter-rouge\">jekyll-graphviz-dot</code>,修改 <code class=\"language-plaintext highlighter-rouge\">Gemfile</code> 增加一句:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>group :jekyll_plugins <span class=\"k\">do\n </span>gem <span class=\"s2\">\"jekyll-graphviz-dot\"</span>\nend\n</code></pre></div></div>\n\n<p>再修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 配置文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-graphviz</span>\n</code></pre></div></div>\n\n<p>再在本地安装 graphviz,可以通过 <code class=\"language-plaintext highlighter-rouge\">conda install graphviz</code> 或者 <code class=\"language-plaintext highlighter-rouge\">brew install graphviz</code>。然后 <code class=\"language-plaintext highlighter-rouge\">bundle install</code> 再 <code class=\"language-plaintext highlighter-rouge\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\n\n<pre><code class=\"language-graphviz\">{% graph some graph title %}\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n{% endgraph %}\n</code></pre>\n\n<p>如果看到如下效果,就说明你都配置成功了:</p>\n\n<div class=\"graphviz-wrapper\">\n\n<!-- Generated by graphviz version 2.43.0 (0)\n -->\n<!-- Title: G Pages: 1 -->\n<svg role=\"img\" aria-label=\"some graph title\" width=\"89pt\" height=\"188pt\" viewBox=\"0.00 0.00 89.00 188.00\">\n<title>some graph title</title>\n<desc>\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n</desc>\n\n<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n<title>G</title>\n<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-184 85,-184 85,4 -4,4\" />\n<!-- a -->\n<g id=\"node1\" class=\"node\">\n<title>a</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-162\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">a</text>\n</g>\n<!-- b -->\n<g id=\"node2\" class=\"node\">\n<title>b</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">b</text>\n</g>\n<!-- a->b -->\n<g id=\"edge1\" class=\"edge\">\n<title>a->b</title>\n<path fill=\"none\" stroke=\"black\" d=\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\" />\n</g>\n<!-- c -->\n<g id=\"node3\" class=\"node\">\n<title>c</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-18\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">c</text>\n</g>\n<!-- b->c -->\n<g id=\"edge2\" class=\"edge\">\n<title>b->c</title>\n<path fill=\"none\" stroke=\"black\" d=\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\" />\n</g>\n<!-- c->a -->\n<g id=\"edge3\" class=\"edge\">\n<title>c->a</title>\n<path fill=\"none\" stroke=\"black\" d=\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\" />\n</g>\n</g>\n</svg>\n</div>\n\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\n\n<h4 id=\"q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</h4>\n\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 呢?方法如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}{%{% endraw %} raw %}\n{% raw %}{%{% endraw %} endraw %}\n</code></pre></div></div>\n\n<p>如上,就是用 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 把 <code class=\"language-plaintext highlighter-rouge\">{%</code> 包起来,但是 <code class=\"language-plaintext highlighter-rouge\">%}</code> 不用包。应该讲的很清楚了吧。</p>\n\n<h3 id=\"参考\">参考</h3>\n\n<ol>\n <li><a href=\"https://bundler.io\">https://bundler.io</a></li>\n <li><a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></li>\n <li><a href=\"https://zhuanlan.zhihu.com/p/87225594\">https://zhuanlan.zhihu.com/p/87225594</a></li>\n <li><a href=\"https://chat.openai.com/chat\">https://chat.openai.com/chat</a></li>\n <li><a href=\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\n <li><a href=\"https://github.com/dyutibarma/monochrome\">https://github.com/dyutibarma/monochrome</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\n <li><a href=\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\n <li><a href=\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"Web":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的 Jekyll 快速教程</title>\n \t<meta name=\"description\" content=\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的 Jekyll 快速教程</h2>\t\t\n\t<time datetime=\"2021-12-23T19:43:02+00:00\" class=\"by-line\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\n\n<h3 id=\"part-1基本特点\">Part 1、基本特点</h3>\n\n<h4 id=\"一基本语法\">一、基本语法</h4>\n\n<ul>\n <li>变量:用双大括号表示变量 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code></li>\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\n <li>支持循环与分支结构:比如 <code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 和 <code class=\"language-plaintext highlighter-rouge\">if-elsif-else-endif</code> :可以使用 <code class=\"language-plaintext highlighter-rouge\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\n</ul>\n\n<h4 id=\"二典型-jekyll-项目结构及重要文件介绍\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\n\n<h5 id=\"1配置文件-_configyml\">1、配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code></h5>\n\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\"language-plaintext highlighter-rouge\">encoding: utf-8</code> 这一条。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Site settings</span>\n<span class=\"na\">encoding</span><span class=\"pi\">:</span> <span class=\"s\">utf-8</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">麦克船长的技术、产品与商业博客</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\"</span>\n<span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptain.com\"</span>\n<span class=\"na\">author</span><span class=\"pi\">:</span>\n <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Your</span><span class=\"nv\"> </span><span class=\"s\">Name\"</span>\n <span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptian.com\"</span>\n</code></pre></div></div>\n\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Markdown and highlighter</span>\n<span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Plugins</span>\n<span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-paginate</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-sitemap</span>\n</code></pre></div></div>\n\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Build settings</span>\n<span class=\"na\">baseurl</span><span class=\"pi\">:</span> <span class=\"c1\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\n<span class=\"na\">source</span><span class=\"pi\">:</span> <span class=\"s\">.</span>\n<span class=\"na\">destination</span><span class=\"pi\">:</span> <span class=\"s\">./_site</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:title</span>\n<span class=\"na\">paginate</span><span class=\"pi\">:</span> <span class=\"m\">20</span>\n<span class=\"na\">paginate_path</span><span class=\"pi\">:</span> <span class=\"s\">/page:num/</span>\n<span class=\"na\">collections</span><span class=\"pi\">:</span>\n <span class=\"na\">posts</span><span class=\"pi\">:</span>\n <span class=\"na\">output</span><span class=\"pi\">:</span> <span class=\"no\">true</span>\n <span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:year/:month/:day/:title/</span>\n <span class=\"na\">directory</span><span class=\"pi\">:</span> <span class=\"s\">_posts</span>\n</code></pre></div></div>\n\n<h5 id=\"2布局文件_layouts-目录下的文件规则\">2、布局文件:<code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的文件规则</h5>\n\n<p>Jekyll 的 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\"language-plaintext highlighter-rouge\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\"language-plaintext highlighter-rouge\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\n\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\"language-plaintext highlighter-rouge\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\n\n<h5 id=\"3页面文件及其头部\">3、页面文件及其头部</h5>\n\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">page</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/categories/</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">Categories</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>每个页面文件的头部都会有layout,并与 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的某个文件对应。</p>\n\n<h3 id=\"part-2jekyll-中的全局变量\">Part 2、Jekyll 中的全局变量</h3>\n\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:包含当前页面或文章的内容。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:包含有关网站的所有标签的信息。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\n</ul>\n\n<p>这些变量可以在模板中使用,比如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><h1></span>{{ page.title }}<span class=\"nt\"></h1></span>\n<span class=\"nt\"><p></span>{{ site.description }}<span class=\"nt\"></p></span>\n<span class=\"nt\"><ul></span>\n {{ for category in site.categories %}\n <span class=\"nt\"><li></span>{{ category }}<span class=\"nt\"></li></span>\n {{ endfor %}\n<span class=\"nt\"></ul></span> \n</code></pre></div></div>\n\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">my_custom_variable</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Hello</span><span class=\"nv\"> </span><span class=\"s\">World\"</span>\n</code></pre></div></div>\n\n<p>然后就可以在模板文件中使用 <code class=\"language-plaintext highlighter-rouge\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\n\n<h4 id=\"一site变量\">一、site变量</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\n\n<h5 id=\"1sitecategories\">1、<code class=\"language-plaintext highlighter-rouge\">site.categories</code></h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\"language-plaintext highlighter-rouge\">first</code> 就是 <code class=\"language-plaintext highlighter-rouge\">category</code> 的名字,如下使用:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>\n</code></pre></div></div>\n\n<h5 id=\"2sitepages\">2、site.pages</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\"language-plaintext highlighter-rouge\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3其他常用属性\">3、其他常用属性</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site.title</code>:是网站的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.related_posts</code>:相关文章的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.data</code>:从 <code class=\"language-plaintext highlighter-rouge\">_data</code> 目录加载的数据。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.static_files</code>:静态文件的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.collections</code>:自定义集合的列表。</li>\n</ul>\n\n<h4 id=\"二page-变量\">二、<code class=\"language-plaintext highlighter-rouge\">page</code> 变量</h4>\n\n<p>在 Jekyll 中,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量属性包括:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">layout</code>:表示页面使用的布局模板的名称。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">title</code>:表示页面的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">date</code>:表示页面的发布日期。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">categories</code>:表示页面所属的分类列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:表示页面所属的标签列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\n</ul>\n\n<p>在模板中,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.属性名 }}</code> 的方式来访问 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.title }}</code>。此外,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量还有其他属性,如 <code class=\"language-plaintext highlighter-rouge\">permalink</code>、<code class=\"language-plaintext highlighter-rouge\">excerpt</code>、<code class=\"language-plaintext highlighter-rouge\">url</code> 等,可以根据需要调用。</p>\n\n<h3 id=\"part-3控制结构\">Part 3、控制结构</h3>\n\n<h4 id=\"1if-else-分支结构\">1、<code class=\"language-plaintext highlighter-rouge\">if-else</code> 分支结构</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ if tmp_var == \"type1\" %}\n{{ elsif tmp_var == \"type2\" %}\n{{ elsif tmp_var == \"type3\" %}\n{{ elsif tmp_var == \"type4\" %}\n{{ else tmp_var == \"type5\" %}\n{{ endif %}\n</code></pre></div></div>\n\n<h4 id=\"2for-endfor-循环结构\">2、<code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 循环结构</h4>\n\n<p>不带条件判断的 <code class=\"language-plaintext highlighter-rouge\">for</code> 循环如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for post in paginator.posts %}\n\t<span class=\"c\"><!-- Your other sentences --></span>\n{{ endfor %}\n</code></pre></div></div>\n\n<p>带条件循环的 <code class=\"language-plaintext highlighter-rouge\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages | where: \"dir\", \"categories\" %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3jekyll-支持的其他结构包括\">3、Jekyll 支持的其他结构包括:</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">capture</code> 用于捕获输出的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">cycle</code> 用于循环一组字符串的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">include</code> 用于包含其他文件的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">while</code> 用于在满足指定条件时执行代码的结构。</li>\n</ul>\n\n<h3 id=\"参考\">参考:</h3>\n\n<p>1、<a href=\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\n2、<a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"AI":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\n \t<meta name=\"description\" content=\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\t\t\n\t<time datetime=\"2022-12-24T15:08:01+00:00\" class=\"by-line\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一自然语言处理领域近年的发展关键节点\" id=\"markdown-toc-一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</a> <ul>\n <li><a href=\"#1从理性主义到经验主义\" id=\"markdown-toc-1从理性主义到经验主义\">1、从理性主义到经验主义</a></li>\n <li><a href=\"#2经验主义的早期还不是深度学习\" id=\"markdown-toc-2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</a></li>\n <li><a href=\"#3撇开特征让机器囫囵吞枣地学吧\" id=\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\n <li><a href=\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\" id=\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\n <li><a href=\"#5自监督学习法让我们省去人工标注\" id=\"markdown-toc-5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</a></li>\n <li><a href=\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\" id=\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\n <li><a href=\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\" id=\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\n <li><a href=\"#8数据和算力有了还不够\" id=\"markdown-toc-8数据和算力有了还不够\">8、数据和算力有了,还不够</a></li>\n </ul>\n </li>\n <li><a href=\"#二学术里程碑几篇重量级论文\" id=\"markdown-toc-二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</a> <ul>\n <li><a href=\"#1提出-transformer-的attention-is-all-you-need2017\" id=\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\n <li><a href=\"#2elmo-deep-contextualized-word-representations\" id=\"markdown-toc-2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</a></li>\n <li><a href=\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\" id=\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\n <li><a href=\"#4gpt-3-language-models-are-few-shot-learners2020\" id=\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\n <li><a href=\"#其他的重量级论文\" id=\"markdown-toc-其他的重量级论文\">其他的重量级论文</a></li>\n </ul>\n </li>\n <li><a href=\"#三行业里程碑\" id=\"markdown-toc-三行业里程碑\">三、行业里程碑</a></li>\n <li><a href=\"#四成本\" id=\"markdown-toc-四成本\">四、成本</a></li>\n <li><a href=\"#五业内应用\" id=\"markdown-toc-五业内应用\">五、业内应用</a></li>\n <li><a href=\"#五行业内哪些人的言论值得我们日常重点关注\" id=\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</h3>\n\n<p><img src=\"/img/src/2022-12-17-ai-bert-1-1.jpg\" alt=\"image\" /></p>\n\n<h4 id=\"1从理性主义到经验主义\">1、从理性主义到经验主义</h4>\n\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\n\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\n\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\n\n<h4 id=\"2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</h4>\n\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\n\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\n\n<h4 id=\"3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\n\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\n\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\n\n<h4 id=\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\n\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\n\n<h4 id=\"5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</h4>\n\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\n\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\n\n<h4 id=\"6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\n\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\n\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\n\n<h4 id=\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\n\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\n\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\n\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\n\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\n\n<h4 id=\"8数据和算力有了还不够\">8、数据和算力有了,还不够</h4>\n\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\n\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\n\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\n\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\n\n<h3 id=\"二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</h3>\n\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\n\n<h4 id=\"1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\n\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\n\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\n\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\n\n<h4 id=\"2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</h4>\n\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\n\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\n\n<h4 id=\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\n\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\n\n<ul>\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\n <li>在预训练中引入自监督学习任务。</li>\n</ul>\n\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\n\n<h4 id=\"4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\n\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\n\n<h4 id=\"其他的重量级论文\">其他的重量级论文</h4>\n\n<ul>\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\n <li>……</li>\n</ul>\n\n<h3 id=\"三行业里程碑\">三、行业里程碑</h3>\n\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\n\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\n\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\n\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\n\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\n\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\n\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\n\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\n\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\n\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\n\n<h3 id=\"四成本\">四、成本</h3>\n\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\n\n<ul>\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\n</ul>\n\n<h3 id=\"五业内应用\">五、业内应用</h3>\n\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\n\n<ul>\n <li>虚拟客服(可以乱真的)</li>\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\n <li>智能翻译</li>\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\n <li>AI 广告公司:替代传统广告公司</li>\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\n</ul>\n\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\n\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\n\n<ul>\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-7.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-2.jpg\" alt=\"image\" /></p>\n\n<ul>\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-8.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年年底,西湖心辰公司发布「<a href=\"https://www.heyfriday.cn/\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-1.png\" alt=\"image\" /></p>\n\n<h3 id=\"五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</h3>\n\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\n\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\n\n<blockquote>\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\n</blockquote>\n\n<blockquote>\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\n</blockquote>\n\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\n\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\n\n<blockquote>\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\n</blockquote>\n\n<blockquote>\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\n</blockquote>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://beta.openai.com/docs/models</li>\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\n <li>https://hub.baai.ac.cn/view/21726</li>\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\n <li>https://www.sohu.com/a/615541698_121255906</li>\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\n \t<meta name=\"description\" content=\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\t\t\n\t<time datetime=\"2022-12-17T15:08:01+00:00\" class=\"by-line\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一关于-bert-的一些背景\" id=\"markdown-toc-一关于-bert-的一些背景\">一、关于 BERT 的一些背景</a></li>\n <li><a href=\"#二开始一个-bert-的动手小试验\" id=\"markdown-toc-二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</a> <ul>\n <li><a href=\"#1安装-anaconda-来为部署-bert-做环境准备\" id=\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\n <li><a href=\"#2安装-bert-所需要的各种依赖\" id=\"markdown-toc-2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</a></li>\n <li><a href=\"#3下载一个预训练pre-train过的-bert-模型\" id=\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\n <li><a href=\"#5启动-bert-服务端\" id=\"markdown-toc-5启动-bert-服务端\">5、启动 BERT 服务端</a></li>\n <li><a href=\"#6在-pycharm-中使用-conda-的环境\" id=\"markdown-toc-6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</a></li>\n <li><a href=\"#7编写程序实现-bert-客户端\" id=\"markdown-toc-7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</a></li>\n </ul>\n </li>\n <li><a href=\"#三bert-模型的优劣势及其原因\" id=\"markdown-toc-三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</a> <ul>\n <li><a href=\"#1bert-的优势是很明显的\" id=\"markdown-toc-1bert-的优势是很明显的\">1、BERT 的优势是很明显的</a> <ul>\n <li><a href=\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\" id=\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\n <li><a href=\"#12识别并专注于较重要的部分进行文本处理\" id=\"markdown-toc-12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\n <li><a href=\"#13快速构建针对具体任务的-nlp-系统\" id=\"markdown-toc-13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\n </ul>\n </li>\n <li><a href=\"#2bert-模型的劣势及其原因\" id=\"markdown-toc-2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</a> <ul>\n <li><a href=\"#21随机挖-mask-的完形填空题是有隐患的\" id=\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\n <li><a href=\"#22nsp-任务有必要吗\" id=\"markdown-toc-22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</a></li>\n <li><a href=\"#23针对两个或以上词组成的连续词的词义被丢失\" id=\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\n <li><a href=\"#24需要的算力高\" id=\"markdown-toc-24需要的算力高\">2.4、需要的算力高</a></li>\n <li><a href=\"#25需要的模型大\" id=\"markdown-toc-25需要的模型大\">2.5、需要的模型大</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四一些关于-bert-的问题\" id=\"markdown-toc-四一些关于-bert-的问题\">四、一些关于 BERT 的问题</a> <ul>\n <li><a href=\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\" id=\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\n <li><a href=\"#2为什么-bert-可以比-rnn-更好地并行化\" id=\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一关于-bert-的一些背景\">一、关于 BERT 的一些背景</h3>\n\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\n\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\n\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\n\n<h3 id=\"二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</h3>\n\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\n\n<h4 id=\"1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\n\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\n\n<p>更新 conda 到最新版本:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda update <span class=\"nt\">-n</span> base conda\n</code></pre></div></div>\n\n<p>使用 Python 3.7 创建一个新的环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda create <span class=\"nt\">-n</span> py37 <span class=\"nv\">python</span><span class=\"o\">=</span>3.7\n</code></pre></div></div>\n\n<p>激活这个新环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda activate py37\n</code></pre></div></div>\n\n<p>验证正在使用的是正确版本的 Python</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>python <span class=\"nt\">--version</span>\n</code></pre></div></div>\n\n<p>另外你可能还会用到的 conda 命令有:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\nconda deactivate py37\n\n<span class=\"c\"># 查看 conda 当前安装的所有库</span>\nconda list\n</code></pre></div></div>\n\n<h4 id=\"2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda <span class=\"nb\">install </span><span class=\"nv\">tensorflow</span><span class=\"o\">==</span>1.14.0\n</code></pre></div></div>\n\n<p>验证 tensorflow 是否安装正确:</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">import</span> <span class=\"nn\">tensorflow</span> <span class=\"k\">as</span> <span class=\"n\">tf</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">tf</span><span class=\"p\">.</span><span class=\"n\">__version__</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<h4 id=\"3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\n\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\n\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\n\n<ul>\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n</ul>\n\n<p>4、安装 BERT 的服务端和客户端</p>\n\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\n\n<p>在你激活的环境里,安装 <code class=\"language-plaintext highlighter-rouge\">bert-as-service</code>:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 安装服务端和客户端</span>\n<span class=\"c\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\nconda <span class=\"nb\">install </span>bert-serving-server bert-serving-client \n验证 bert-as-service 是否安装成功\nbert-serving-start <span class=\"nt\">-h</span>\n</code></pre></div></div>\n\n<h4 id=\"5启动-bert-服务端\">5、启动 BERT 服务端</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 命令行下启动BERT服务</span>\n<span class=\"c\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\nbert-serving-start <span class=\"nt\">-model_dir</span> /模型/的/绝对/路径 <span class=\"nt\">-num_worker</span><span class=\"o\">=</span>4\n</code></pre></div></div>\n\n<h4 id=\"6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</h4>\n\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\n\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\"language-plaintext highlighter-rouge\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\n\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\n\n<h4 id=\"7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</h4>\n\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">from</span> <span class=\"nn\">bert_serving.client</span> <span class=\"kn\">import</span> <span class=\"n\">BertClient</span>\n<span class=\"kn\">import</span> <span class=\"nn\">numpy</span> <span class=\"k\">as</span> <span class=\"n\">np</span>\n\n<span class=\"c1\"># 定义类\n</span><span class=\"k\">class</span> <span class=\"nc\">BertModel</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"nf\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span> <span class=\"o\">=</span> <span class=\"n\">BertClient</span><span class=\"p\">(</span><span class=\"n\">ip</span><span class=\"o\">=</span><span class=\"s\">'127.0.0.1'</span><span class=\"p\">,</span> <span class=\"n\">port</span><span class=\"o\">=</span><span class=\"mi\">5555</span><span class=\"p\">,</span> <span class=\"n\">port_out</span><span class=\"o\">=</span><span class=\"mi\">5556</span><span class=\"p\">)</span> <span class=\"c1\"># 创建客户端对象\n</span> <span class=\"c1\"># 注意:可以参考API,查看其它参数的设置\n</span> <span class=\"c1\"># 127.0.0.1 表示本机IP,也可以用localhost\n</span> <span class=\"k\">except</span><span class=\"p\">:</span>\n <span class=\"k\">raise</span> <span class=\"nb\">Exception</span><span class=\"p\">(</span><span class=\"s\">\"cannot create BertClient\"</span><span class=\"p\">)</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">close_bert</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">sentence_embedding</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">text</span><span class=\"p\">):</span>\n <span class=\"s\">'''对输入文本进行embedding\n Args:\n text: str, 输入文本\n Returns:\n text_vector: float, 返回一个列表,包含text的embedding编码值\n '''</span>\n <span class=\"n\">text_vector</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">encode</span><span class=\"p\">([</span><span class=\"n\">text</span><span class=\"p\">])[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"k\">return</span> <span class=\"n\">text_vector</span> <span class=\"c1\"># 获取输出结果\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">caculate_similarity</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">vec_1</span><span class=\"p\">,</span> <span class=\"n\">vec_2</span><span class=\"p\">):</span>\n <span class=\"s\">'''根据两个语句的vector,计算它们的相似性\n Args:\n vec_1: float, 语句1的vector\n vec_2: float, 语句2的vector\n Returns:\n sim_value: float, 返回相似性的计算值\n '''</span>\n <span class=\"c1\"># 根据cosine的计算公式\n</span> <span class=\"n\">v1</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_1</span><span class=\"p\">)</span>\n <span class=\"n\">v2</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_2</span><span class=\"p\">)</span>\n <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">v1</span> <span class=\"o\">*</span> <span class=\"n\">v2</span><span class=\"p\">.</span><span class=\"n\">T</span><span class=\"p\">)</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v1</span><span class=\"p\">)</span> <span class=\"o\">*</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v2</span><span class=\"p\">)</span>\n <span class=\"n\">cosine</span> <span class=\"o\">=</span> <span class=\"n\">a</span> <span class=\"o\">/</span> <span class=\"n\">b</span>\n <span class=\"k\">return</span> <span class=\"n\">cosine</span>\n\n\n<span class=\"k\">if</span> <span class=\"n\">__name__</span> <span class=\"o\">==</span> <span class=\"s\">\"__main__\"</span><span class=\"p\">:</span>\n <span class=\"c1\"># 创建bert对象\n</span> <span class=\"n\">bert</span> <span class=\"o\">=</span> <span class=\"n\">BertModel</span><span class=\"p\">()</span>\n <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n <span class=\"c1\"># --- 输入语句 ----\n</span> <span class=\"n\">input_a</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句1: '</span><span class=\"p\">)</span>\n\n <span class=\"k\">if</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"N\"</span> <span class=\"ow\">or</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"n\"</span><span class=\"p\">:</span>\n <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">close_bert</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span> <span class=\"k\">break</span>\n\n <span class=\"n\">input_b</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句2: '</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># --- 对输入语句进行embedding ---\n</span>\n <span class=\"n\">a_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_a</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'a_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">a_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"n\">b_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_b</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'b_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 计算两个语句的相似性\n</span> <span class=\"n\">cos</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">caculate_similarity</span><span class=\"p\">(</span><span class=\"n\">a_vec</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'cosine value : '</span><span class=\"p\">,</span> <span class=\"n\">cos</span><span class=\"p\">)</span>\n\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'</span><span class=\"se\">\\n\\n</span><span class=\"s\">'</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\n</span> <span class=\"k\">if</span> <span class=\"n\">cos</span> <span class=\"o\">></span> <span class=\"mf\">0.85</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"2个语句的含义相似\"</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"不相似\"</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<p>在使用 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 连接 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 时,你需要确保 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 使用的模型和 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\n\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>请输入语句1: \n请输入语句2: \n</code></pre></div></div>\n\n<p>两句输入好确认后,得到如下形式的结果:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>a_vec shape : (768,)\nb_vec shape : (768,)\ncosine value : 0.8691698561422959\n</code></pre></div></div>\n\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\n\n<h3 id=\"三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</h3>\n\n<p>论文地址:<a href=\"https://arxiv.org/abs/1810.04805\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\n\n<h4 id=\"1bert-的优势是很明显的\">1、BERT 的优势是很明显的</h4>\n\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\n\n<blockquote>\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\n</blockquote>\n\n<h5 id=\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\n\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\n\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\n\n<h5 id=\"12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</h5>\n\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\n\n<h5 id=\"13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</h5>\n\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\n\n<h4 id=\"2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</h4>\n\n<h5 id=\"21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\n\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\n\n<h5 id=\"22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</h5>\n\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\n\n<h5 id=\"23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\n\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\n\n<h5 id=\"24需要的算力高\">2.4、需要的算力高</h5>\n\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\n\n<h5 id=\"25需要的模型大\">2.5、需要的模型大</h5>\n\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\n\n<h3 id=\"四一些关于-bert-的问题\">四、一些关于 BERT 的问题</h3>\n\n<h4 id=\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\n\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\n\n<h4 id=\"2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\n\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://arxiv.org/abs/1810.04805</li>\n <li>https://github.com/google-research/bert</li>\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\n \t<meta name=\"description\" content=\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\t\t\n\t<time datetime=\"2022-12-11T15:59:57+00:00\" class=\"by-line\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2022-12-11-wechat-chatgpt-3.png\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\n\n<h3 id=\"stepbystep\">Step by step</h3>\n\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\n\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\n\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\n\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\n\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\n\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\n\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">chatGPTAccountPool</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"na\">email</span><span class=\"pi\">:</span> <span class=\"s\"><your email></span>\n <span class=\"na\">password</span><span class=\"pi\">:</span> <span class=\"s\"><your password></span>\n<span class=\"c1\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\n<span class=\"na\">chatPrivateTiggerKeyword</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">\"</span>\n</code></pre></div></div>\n\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker pull holegots/wechat-chatgpt:latest。\n</code></pre></div></div>\n\n<p>第八步,启动 wechat-chatgpt:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker run <span class=\"nt\">-d</span> <span class=\"nt\">--name</span> wechat-chatgpt <span class=\"nt\">-v</span> <span class=\"si\">$(</span><span class=\"nb\">pwd</span><span class=\"si\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\n</code></pre></div></div>\n\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\"language-plaintext highlighter-rouge\">npm install && poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-1.png\" alt=\"image\" style=\"width:100%\" /></th>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-2.png\" alt=\"image\" style=\"width:100%\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\n\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker logs <span class=\"nt\">-f</span> wechat-chatgpt\n</code></pre></div></div>\n\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\n\n<p>如果要停止运行,用如下命令:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker stop wechat-chatgpt\n</code></pre></div></div>\n\n<h3 id=\"参考\">参考</h3>\n\n<p>1、<a href=\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"人工智能":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\n \t<meta name=\"description\" content=\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\t\t\n\t<time datetime=\"2022-12-24T15:08:01+00:00\" class=\"by-line\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一自然语言处理领域近年的发展关键节点\" id=\"markdown-toc-一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</a> <ul>\n <li><a href=\"#1从理性主义到经验主义\" id=\"markdown-toc-1从理性主义到经验主义\">1、从理性主义到经验主义</a></li>\n <li><a href=\"#2经验主义的早期还不是深度学习\" id=\"markdown-toc-2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</a></li>\n <li><a href=\"#3撇开特征让机器囫囵吞枣地学吧\" id=\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\n <li><a href=\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\" id=\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\n <li><a href=\"#5自监督学习法让我们省去人工标注\" id=\"markdown-toc-5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</a></li>\n <li><a href=\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\" id=\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\n <li><a href=\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\" id=\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\n <li><a href=\"#8数据和算力有了还不够\" id=\"markdown-toc-8数据和算力有了还不够\">8、数据和算力有了,还不够</a></li>\n </ul>\n </li>\n <li><a href=\"#二学术里程碑几篇重量级论文\" id=\"markdown-toc-二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</a> <ul>\n <li><a href=\"#1提出-transformer-的attention-is-all-you-need2017\" id=\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\n <li><a href=\"#2elmo-deep-contextualized-word-representations\" id=\"markdown-toc-2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</a></li>\n <li><a href=\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\" id=\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\n <li><a href=\"#4gpt-3-language-models-are-few-shot-learners2020\" id=\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\n <li><a href=\"#其他的重量级论文\" id=\"markdown-toc-其他的重量级论文\">其他的重量级论文</a></li>\n </ul>\n </li>\n <li><a href=\"#三行业里程碑\" id=\"markdown-toc-三行业里程碑\">三、行业里程碑</a></li>\n <li><a href=\"#四成本\" id=\"markdown-toc-四成本\">四、成本</a></li>\n <li><a href=\"#五业内应用\" id=\"markdown-toc-五业内应用\">五、业内应用</a></li>\n <li><a href=\"#五行业内哪些人的言论值得我们日常重点关注\" id=\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</h3>\n\n<p><img src=\"/img/src/2022-12-17-ai-bert-1-1.jpg\" alt=\"image\" /></p>\n\n<h4 id=\"1从理性主义到经验主义\">1、从理性主义到经验主义</h4>\n\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\n\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\n\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\n\n<h4 id=\"2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</h4>\n\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\n\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\n\n<h4 id=\"3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\n\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\n\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\n\n<h4 id=\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\n\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\n\n<h4 id=\"5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</h4>\n\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\n\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\n\n<h4 id=\"6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\n\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\n\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\n\n<h4 id=\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\n\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\n\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\n\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\n\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\n\n<h4 id=\"8数据和算力有了还不够\">8、数据和算力有了,还不够</h4>\n\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\n\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\n\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\n\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\n\n<h3 id=\"二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</h3>\n\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\n\n<h4 id=\"1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\n\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\n\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\n\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\n\n<h4 id=\"2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</h4>\n\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\n\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\n\n<h4 id=\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\n\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\n\n<ul>\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\n <li>在预训练中引入自监督学习任务。</li>\n</ul>\n\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\n\n<h4 id=\"4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\n\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\n\n<h4 id=\"其他的重量级论文\">其他的重量级论文</h4>\n\n<ul>\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\n <li>……</li>\n</ul>\n\n<h3 id=\"三行业里程碑\">三、行业里程碑</h3>\n\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\n\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\n\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\n\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\n\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\n\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\n\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\n\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\n\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\n\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\n\n<h3 id=\"四成本\">四、成本</h3>\n\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\n\n<ul>\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\n</ul>\n\n<h3 id=\"五业内应用\">五、业内应用</h3>\n\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\n\n<ul>\n <li>虚拟客服(可以乱真的)</li>\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\n <li>智能翻译</li>\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\n <li>AI 广告公司:替代传统广告公司</li>\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\n</ul>\n\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\n\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\n\n<ul>\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-7.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-2.jpg\" alt=\"image\" /></p>\n\n<ul>\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-8.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年年底,西湖心辰公司发布「<a href=\"https://www.heyfriday.cn/\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-1.png\" alt=\"image\" /></p>\n\n<h3 id=\"五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</h3>\n\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\n\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\n\n<blockquote>\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\n</blockquote>\n\n<blockquote>\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\n</blockquote>\n\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\n\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\n\n<blockquote>\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\n</blockquote>\n\n<blockquote>\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\n</blockquote>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://beta.openai.com/docs/models</li>\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\n <li>https://hub.baai.ac.cn/view/21726</li>\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\n <li>https://www.sohu.com/a/615541698_121255906</li>\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\n \t<meta name=\"description\" content=\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\t\t\n\t<time datetime=\"2022-12-17T15:08:01+00:00\" class=\"by-line\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一关于-bert-的一些背景\" id=\"markdown-toc-一关于-bert-的一些背景\">一、关于 BERT 的一些背景</a></li>\n <li><a href=\"#二开始一个-bert-的动手小试验\" id=\"markdown-toc-二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</a> <ul>\n <li><a href=\"#1安装-anaconda-来为部署-bert-做环境准备\" id=\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\n <li><a href=\"#2安装-bert-所需要的各种依赖\" id=\"markdown-toc-2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</a></li>\n <li><a href=\"#3下载一个预训练pre-train过的-bert-模型\" id=\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\n <li><a href=\"#5启动-bert-服务端\" id=\"markdown-toc-5启动-bert-服务端\">5、启动 BERT 服务端</a></li>\n <li><a href=\"#6在-pycharm-中使用-conda-的环境\" id=\"markdown-toc-6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</a></li>\n <li><a href=\"#7编写程序实现-bert-客户端\" id=\"markdown-toc-7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</a></li>\n </ul>\n </li>\n <li><a href=\"#三bert-模型的优劣势及其原因\" id=\"markdown-toc-三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</a> <ul>\n <li><a href=\"#1bert-的优势是很明显的\" id=\"markdown-toc-1bert-的优势是很明显的\">1、BERT 的优势是很明显的</a> <ul>\n <li><a href=\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\" id=\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\n <li><a href=\"#12识别并专注于较重要的部分进行文本处理\" id=\"markdown-toc-12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\n <li><a href=\"#13快速构建针对具体任务的-nlp-系统\" id=\"markdown-toc-13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\n </ul>\n </li>\n <li><a href=\"#2bert-模型的劣势及其原因\" id=\"markdown-toc-2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</a> <ul>\n <li><a href=\"#21随机挖-mask-的完形填空题是有隐患的\" id=\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\n <li><a href=\"#22nsp-任务有必要吗\" id=\"markdown-toc-22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</a></li>\n <li><a href=\"#23针对两个或以上词组成的连续词的词义被丢失\" id=\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\n <li><a href=\"#24需要的算力高\" id=\"markdown-toc-24需要的算力高\">2.4、需要的算力高</a></li>\n <li><a href=\"#25需要的模型大\" id=\"markdown-toc-25需要的模型大\">2.5、需要的模型大</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四一些关于-bert-的问题\" id=\"markdown-toc-四一些关于-bert-的问题\">四、一些关于 BERT 的问题</a> <ul>\n <li><a href=\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\" id=\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\n <li><a href=\"#2为什么-bert-可以比-rnn-更好地并行化\" id=\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一关于-bert-的一些背景\">一、关于 BERT 的一些背景</h3>\n\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\n\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\n\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\n\n<h3 id=\"二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</h3>\n\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\n\n<h4 id=\"1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\n\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\n\n<p>更新 conda 到最新版本:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda update <span class=\"nt\">-n</span> base conda\n</code></pre></div></div>\n\n<p>使用 Python 3.7 创建一个新的环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda create <span class=\"nt\">-n</span> py37 <span class=\"nv\">python</span><span class=\"o\">=</span>3.7\n</code></pre></div></div>\n\n<p>激活这个新环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda activate py37\n</code></pre></div></div>\n\n<p>验证正在使用的是正确版本的 Python</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>python <span class=\"nt\">--version</span>\n</code></pre></div></div>\n\n<p>另外你可能还会用到的 conda 命令有:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\nconda deactivate py37\n\n<span class=\"c\"># 查看 conda 当前安装的所有库</span>\nconda list\n</code></pre></div></div>\n\n<h4 id=\"2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda <span class=\"nb\">install </span><span class=\"nv\">tensorflow</span><span class=\"o\">==</span>1.14.0\n</code></pre></div></div>\n\n<p>验证 tensorflow 是否安装正确:</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">import</span> <span class=\"nn\">tensorflow</span> <span class=\"k\">as</span> <span class=\"n\">tf</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">tf</span><span class=\"p\">.</span><span class=\"n\">__version__</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<h4 id=\"3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\n\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\n\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\n\n<ul>\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n</ul>\n\n<p>4、安装 BERT 的服务端和客户端</p>\n\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\n\n<p>在你激活的环境里,安装 <code class=\"language-plaintext highlighter-rouge\">bert-as-service</code>:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 安装服务端和客户端</span>\n<span class=\"c\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\nconda <span class=\"nb\">install </span>bert-serving-server bert-serving-client \n验证 bert-as-service 是否安装成功\nbert-serving-start <span class=\"nt\">-h</span>\n</code></pre></div></div>\n\n<h4 id=\"5启动-bert-服务端\">5、启动 BERT 服务端</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 命令行下启动BERT服务</span>\n<span class=\"c\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\nbert-serving-start <span class=\"nt\">-model_dir</span> /模型/的/绝对/路径 <span class=\"nt\">-num_worker</span><span class=\"o\">=</span>4\n</code></pre></div></div>\n\n<h4 id=\"6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</h4>\n\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\n\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\"language-plaintext highlighter-rouge\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\n\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\n\n<h4 id=\"7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</h4>\n\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">from</span> <span class=\"nn\">bert_serving.client</span> <span class=\"kn\">import</span> <span class=\"n\">BertClient</span>\n<span class=\"kn\">import</span> <span class=\"nn\">numpy</span> <span class=\"k\">as</span> <span class=\"n\">np</span>\n\n<span class=\"c1\"># 定义类\n</span><span class=\"k\">class</span> <span class=\"nc\">BertModel</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"nf\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span> <span class=\"o\">=</span> <span class=\"n\">BertClient</span><span class=\"p\">(</span><span class=\"n\">ip</span><span class=\"o\">=</span><span class=\"s\">'127.0.0.1'</span><span class=\"p\">,</span> <span class=\"n\">port</span><span class=\"o\">=</span><span class=\"mi\">5555</span><span class=\"p\">,</span> <span class=\"n\">port_out</span><span class=\"o\">=</span><span class=\"mi\">5556</span><span class=\"p\">)</span> <span class=\"c1\"># 创建客户端对象\n</span> <span class=\"c1\"># 注意:可以参考API,查看其它参数的设置\n</span> <span class=\"c1\"># 127.0.0.1 表示本机IP,也可以用localhost\n</span> <span class=\"k\">except</span><span class=\"p\">:</span>\n <span class=\"k\">raise</span> <span class=\"nb\">Exception</span><span class=\"p\">(</span><span class=\"s\">\"cannot create BertClient\"</span><span class=\"p\">)</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">close_bert</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">sentence_embedding</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">text</span><span class=\"p\">):</span>\n <span class=\"s\">'''对输入文本进行embedding\n Args:\n text: str, 输入文本\n Returns:\n text_vector: float, 返回一个列表,包含text的embedding编码值\n '''</span>\n <span class=\"n\">text_vector</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">encode</span><span class=\"p\">([</span><span class=\"n\">text</span><span class=\"p\">])[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"k\">return</span> <span class=\"n\">text_vector</span> <span class=\"c1\"># 获取输出结果\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">caculate_similarity</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">vec_1</span><span class=\"p\">,</span> <span class=\"n\">vec_2</span><span class=\"p\">):</span>\n <span class=\"s\">'''根据两个语句的vector,计算它们的相似性\n Args:\n vec_1: float, 语句1的vector\n vec_2: float, 语句2的vector\n Returns:\n sim_value: float, 返回相似性的计算值\n '''</span>\n <span class=\"c1\"># 根据cosine的计算公式\n</span> <span class=\"n\">v1</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_1</span><span class=\"p\">)</span>\n <span class=\"n\">v2</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_2</span><span class=\"p\">)</span>\n <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">v1</span> <span class=\"o\">*</span> <span class=\"n\">v2</span><span class=\"p\">.</span><span class=\"n\">T</span><span class=\"p\">)</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v1</span><span class=\"p\">)</span> <span class=\"o\">*</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v2</span><span class=\"p\">)</span>\n <span class=\"n\">cosine</span> <span class=\"o\">=</span> <span class=\"n\">a</span> <span class=\"o\">/</span> <span class=\"n\">b</span>\n <span class=\"k\">return</span> <span class=\"n\">cosine</span>\n\n\n<span class=\"k\">if</span> <span class=\"n\">__name__</span> <span class=\"o\">==</span> <span class=\"s\">\"__main__\"</span><span class=\"p\">:</span>\n <span class=\"c1\"># 创建bert对象\n</span> <span class=\"n\">bert</span> <span class=\"o\">=</span> <span class=\"n\">BertModel</span><span class=\"p\">()</span>\n <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n <span class=\"c1\"># --- 输入语句 ----\n</span> <span class=\"n\">input_a</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句1: '</span><span class=\"p\">)</span>\n\n <span class=\"k\">if</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"N\"</span> <span class=\"ow\">or</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"n\"</span><span class=\"p\">:</span>\n <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">close_bert</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span> <span class=\"k\">break</span>\n\n <span class=\"n\">input_b</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句2: '</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># --- 对输入语句进行embedding ---\n</span>\n <span class=\"n\">a_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_a</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'a_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">a_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"n\">b_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_b</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'b_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 计算两个语句的相似性\n</span> <span class=\"n\">cos</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">caculate_similarity</span><span class=\"p\">(</span><span class=\"n\">a_vec</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'cosine value : '</span><span class=\"p\">,</span> <span class=\"n\">cos</span><span class=\"p\">)</span>\n\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'</span><span class=\"se\">\\n\\n</span><span class=\"s\">'</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\n</span> <span class=\"k\">if</span> <span class=\"n\">cos</span> <span class=\"o\">></span> <span class=\"mf\">0.85</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"2个语句的含义相似\"</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"不相似\"</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<p>在使用 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 连接 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 时,你需要确保 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 使用的模型和 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\n\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>请输入语句1: \n请输入语句2: \n</code></pre></div></div>\n\n<p>两句输入好确认后,得到如下形式的结果:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>a_vec shape : (768,)\nb_vec shape : (768,)\ncosine value : 0.8691698561422959\n</code></pre></div></div>\n\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\n\n<h3 id=\"三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</h3>\n\n<p>论文地址:<a href=\"https://arxiv.org/abs/1810.04805\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\n\n<h4 id=\"1bert-的优势是很明显的\">1、BERT 的优势是很明显的</h4>\n\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\n\n<blockquote>\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\n</blockquote>\n\n<h5 id=\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\n\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\n\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\n\n<h5 id=\"12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</h5>\n\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\n\n<h5 id=\"13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</h5>\n\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\n\n<h4 id=\"2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</h4>\n\n<h5 id=\"21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\n\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\n\n<h5 id=\"22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</h5>\n\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\n\n<h5 id=\"23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\n\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\n\n<h5 id=\"24需要的算力高\">2.4、需要的算力高</h5>\n\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\n\n<h5 id=\"25需要的模型大\">2.5、需要的模型大</h5>\n\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\n\n<h3 id=\"四一些关于-bert-的问题\">四、一些关于 BERT 的问题</h3>\n\n<h4 id=\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\n\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\n\n<h4 id=\"2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\n\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://arxiv.org/abs/1810.04805</li>\n <li>https://github.com/google-research/bert</li>\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\n \t<meta name=\"description\" content=\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\t\t\n\t<time datetime=\"2022-12-11T15:59:57+00:00\" class=\"by-line\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2022-12-11-wechat-chatgpt-3.png\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\n\n<h3 id=\"stepbystep\">Step by step</h3>\n\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\n\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\n\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\n\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\n\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\n\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\n\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">chatGPTAccountPool</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"na\">email</span><span class=\"pi\">:</span> <span class=\"s\"><your email></span>\n <span class=\"na\">password</span><span class=\"pi\">:</span> <span class=\"s\"><your password></span>\n<span class=\"c1\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\n<span class=\"na\">chatPrivateTiggerKeyword</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">\"</span>\n</code></pre></div></div>\n\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker pull holegots/wechat-chatgpt:latest。\n</code></pre></div></div>\n\n<p>第八步,启动 wechat-chatgpt:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker run <span class=\"nt\">-d</span> <span class=\"nt\">--name</span> wechat-chatgpt <span class=\"nt\">-v</span> <span class=\"si\">$(</span><span class=\"nb\">pwd</span><span class=\"si\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\n</code></pre></div></div>\n\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\"language-plaintext highlighter-rouge\">npm install && poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-1.png\" alt=\"image\" style=\"width:100%\" /></th>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-2.png\" alt=\"image\" style=\"width:100%\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\n\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker logs <span class=\"nt\">-f</span> wechat-chatgpt\n</code></pre></div></div>\n\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\n\n<p>如果要停止运行,用如下命令:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker stop wechat-chatgpt\n</code></pre></div></div>\n\n<h3 id=\"参考\">参考</h3>\n\n<p>1、<a href=\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"diffusion":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"MidJourney":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"Text2Image":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"文生图":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"AIGC":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"ChatGPT":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\n \t<meta name=\"description\" content=\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\t\t\n\t<time datetime=\"2022-12-11T15:59:57+00:00\" class=\"by-line\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2022-12-11-wechat-chatgpt-3.png\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\n\n<h3 id=\"stepbystep\">Step by step</h3>\n\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\n\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\n\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\n\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\n\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\n\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\n\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">chatGPTAccountPool</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"na\">email</span><span class=\"pi\">:</span> <span class=\"s\"><your email></span>\n <span class=\"na\">password</span><span class=\"pi\">:</span> <span class=\"s\"><your password></span>\n<span class=\"c1\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\n<span class=\"na\">chatPrivateTiggerKeyword</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">\"</span>\n</code></pre></div></div>\n\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker pull holegots/wechat-chatgpt:latest。\n</code></pre></div></div>\n\n<p>第八步,启动 wechat-chatgpt:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker run <span class=\"nt\">-d</span> <span class=\"nt\">--name</span> wechat-chatgpt <span class=\"nt\">-v</span> <span class=\"si\">$(</span><span class=\"nb\">pwd</span><span class=\"si\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\n</code></pre></div></div>\n\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\"language-plaintext highlighter-rouge\">npm install && poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-1.png\" alt=\"image\" style=\"width:100%\" /></th>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-2.png\" alt=\"image\" style=\"width:100%\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\n\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker logs <span class=\"nt\">-f</span> wechat-chatgpt\n</code></pre></div></div>\n\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\n\n<p>如果要停止运行,用如下命令:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker stop wechat-chatgpt\n</code></pre></div></div>\n\n<h3 id=\"参考\">参考</h3>\n\n<p>1、<a href=\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"OpenAI":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\n \t<meta name=\"description\" content=\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\t\t\n\t<time datetime=\"2022-12-11T15:59:57+00:00\" class=\"by-line\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2022-12-11-wechat-chatgpt-3.png\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\n\n<h3 id=\"stepbystep\">Step by step</h3>\n\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\n\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\n\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\n\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\n\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\n\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\n\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">chatGPTAccountPool</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"na\">email</span><span class=\"pi\">:</span> <span class=\"s\"><your email></span>\n <span class=\"na\">password</span><span class=\"pi\">:</span> <span class=\"s\"><your password></span>\n<span class=\"c1\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\n<span class=\"na\">chatPrivateTiggerKeyword</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">\"</span>\n</code></pre></div></div>\n\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker pull holegots/wechat-chatgpt:latest。\n</code></pre></div></div>\n\n<p>第八步,启动 wechat-chatgpt:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker run <span class=\"nt\">-d</span> <span class=\"nt\">--name</span> wechat-chatgpt <span class=\"nt\">-v</span> <span class=\"si\">$(</span><span class=\"nb\">pwd</span><span class=\"si\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\n</code></pre></div></div>\n\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\"language-plaintext highlighter-rouge\">npm install && poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-1.png\" alt=\"image\" style=\"width:100%\" /></th>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-2.png\" alt=\"image\" style=\"width:100%\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\n\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker logs <span class=\"nt\">-f</span> wechat-chatgpt\n</code></pre></div></div>\n\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\n\n<p>如果要停止运行,用如下命令:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker stop wechat-chatgpt\n</code></pre></div></div>\n\n<h3 id=\"参考\">参考</h3>\n\n<p>1、<a href=\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"微信":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\n \t<meta name=\"description\" content=\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\t\t\n\t<time datetime=\"2022-12-11T15:59:57+00:00\" class=\"by-line\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2022-12-11-wechat-chatgpt-3.png\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\n\n<h3 id=\"stepbystep\">Step by step</h3>\n\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\n\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\n\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\n\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\n\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\n\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\n\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">chatGPTAccountPool</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"na\">email</span><span class=\"pi\">:</span> <span class=\"s\"><your email></span>\n <span class=\"na\">password</span><span class=\"pi\">:</span> <span class=\"s\"><your password></span>\n<span class=\"c1\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\n<span class=\"na\">chatPrivateTiggerKeyword</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">\"</span>\n</code></pre></div></div>\n\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker pull holegots/wechat-chatgpt:latest。\n</code></pre></div></div>\n\n<p>第八步,启动 wechat-chatgpt:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker run <span class=\"nt\">-d</span> <span class=\"nt\">--name</span> wechat-chatgpt <span class=\"nt\">-v</span> <span class=\"si\">$(</span><span class=\"nb\">pwd</span><span class=\"si\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\n</code></pre></div></div>\n\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\"language-plaintext highlighter-rouge\">npm install && poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-1.png\" alt=\"image\" style=\"width:100%\" /></th>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-2.png\" alt=\"image\" style=\"width:100%\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\n\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker logs <span class=\"nt\">-f</span> wechat-chatgpt\n</code></pre></div></div>\n\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\n\n<p>如果要停止运行,用如下命令:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker stop wechat-chatgpt\n</code></pre></div></div>\n\n<h3 id=\"参考\">参考</h3>\n\n<p>1、<a href=\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"BERT":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\n \t<meta name=\"description\" content=\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\t\t\n\t<time datetime=\"2022-12-17T15:08:01+00:00\" class=\"by-line\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一关于-bert-的一些背景\" id=\"markdown-toc-一关于-bert-的一些背景\">一、关于 BERT 的一些背景</a></li>\n <li><a href=\"#二开始一个-bert-的动手小试验\" id=\"markdown-toc-二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</a> <ul>\n <li><a href=\"#1安装-anaconda-来为部署-bert-做环境准备\" id=\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\n <li><a href=\"#2安装-bert-所需要的各种依赖\" id=\"markdown-toc-2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</a></li>\n <li><a href=\"#3下载一个预训练pre-train过的-bert-模型\" id=\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\n <li><a href=\"#5启动-bert-服务端\" id=\"markdown-toc-5启动-bert-服务端\">5、启动 BERT 服务端</a></li>\n <li><a href=\"#6在-pycharm-中使用-conda-的环境\" id=\"markdown-toc-6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</a></li>\n <li><a href=\"#7编写程序实现-bert-客户端\" id=\"markdown-toc-7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</a></li>\n </ul>\n </li>\n <li><a href=\"#三bert-模型的优劣势及其原因\" id=\"markdown-toc-三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</a> <ul>\n <li><a href=\"#1bert-的优势是很明显的\" id=\"markdown-toc-1bert-的优势是很明显的\">1、BERT 的优势是很明显的</a> <ul>\n <li><a href=\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\" id=\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\n <li><a href=\"#12识别并专注于较重要的部分进行文本处理\" id=\"markdown-toc-12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\n <li><a href=\"#13快速构建针对具体任务的-nlp-系统\" id=\"markdown-toc-13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\n </ul>\n </li>\n <li><a href=\"#2bert-模型的劣势及其原因\" id=\"markdown-toc-2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</a> <ul>\n <li><a href=\"#21随机挖-mask-的完形填空题是有隐患的\" id=\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\n <li><a href=\"#22nsp-任务有必要吗\" id=\"markdown-toc-22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</a></li>\n <li><a href=\"#23针对两个或以上词组成的连续词的词义被丢失\" id=\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\n <li><a href=\"#24需要的算力高\" id=\"markdown-toc-24需要的算力高\">2.4、需要的算力高</a></li>\n <li><a href=\"#25需要的模型大\" id=\"markdown-toc-25需要的模型大\">2.5、需要的模型大</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四一些关于-bert-的问题\" id=\"markdown-toc-四一些关于-bert-的问题\">四、一些关于 BERT 的问题</a> <ul>\n <li><a href=\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\" id=\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\n <li><a href=\"#2为什么-bert-可以比-rnn-更好地并行化\" id=\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一关于-bert-的一些背景\">一、关于 BERT 的一些背景</h3>\n\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\n\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\n\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\n\n<h3 id=\"二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</h3>\n\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\n\n<h4 id=\"1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\n\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\n\n<p>更新 conda 到最新版本:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda update <span class=\"nt\">-n</span> base conda\n</code></pre></div></div>\n\n<p>使用 Python 3.7 创建一个新的环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda create <span class=\"nt\">-n</span> py37 <span class=\"nv\">python</span><span class=\"o\">=</span>3.7\n</code></pre></div></div>\n\n<p>激活这个新环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda activate py37\n</code></pre></div></div>\n\n<p>验证正在使用的是正确版本的 Python</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>python <span class=\"nt\">--version</span>\n</code></pre></div></div>\n\n<p>另外你可能还会用到的 conda 命令有:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\nconda deactivate py37\n\n<span class=\"c\"># 查看 conda 当前安装的所有库</span>\nconda list\n</code></pre></div></div>\n\n<h4 id=\"2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda <span class=\"nb\">install </span><span class=\"nv\">tensorflow</span><span class=\"o\">==</span>1.14.0\n</code></pre></div></div>\n\n<p>验证 tensorflow 是否安装正确:</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">import</span> <span class=\"nn\">tensorflow</span> <span class=\"k\">as</span> <span class=\"n\">tf</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">tf</span><span class=\"p\">.</span><span class=\"n\">__version__</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<h4 id=\"3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\n\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\n\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\n\n<ul>\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n</ul>\n\n<p>4、安装 BERT 的服务端和客户端</p>\n\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\n\n<p>在你激活的环境里,安装 <code class=\"language-plaintext highlighter-rouge\">bert-as-service</code>:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 安装服务端和客户端</span>\n<span class=\"c\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\nconda <span class=\"nb\">install </span>bert-serving-server bert-serving-client \n验证 bert-as-service 是否安装成功\nbert-serving-start <span class=\"nt\">-h</span>\n</code></pre></div></div>\n\n<h4 id=\"5启动-bert-服务端\">5、启动 BERT 服务端</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 命令行下启动BERT服务</span>\n<span class=\"c\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\nbert-serving-start <span class=\"nt\">-model_dir</span> /模型/的/绝对/路径 <span class=\"nt\">-num_worker</span><span class=\"o\">=</span>4\n</code></pre></div></div>\n\n<h4 id=\"6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</h4>\n\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\n\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\"language-plaintext highlighter-rouge\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\n\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\n\n<h4 id=\"7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</h4>\n\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">from</span> <span class=\"nn\">bert_serving.client</span> <span class=\"kn\">import</span> <span class=\"n\">BertClient</span>\n<span class=\"kn\">import</span> <span class=\"nn\">numpy</span> <span class=\"k\">as</span> <span class=\"n\">np</span>\n\n<span class=\"c1\"># 定义类\n</span><span class=\"k\">class</span> <span class=\"nc\">BertModel</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"nf\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span> <span class=\"o\">=</span> <span class=\"n\">BertClient</span><span class=\"p\">(</span><span class=\"n\">ip</span><span class=\"o\">=</span><span class=\"s\">'127.0.0.1'</span><span class=\"p\">,</span> <span class=\"n\">port</span><span class=\"o\">=</span><span class=\"mi\">5555</span><span class=\"p\">,</span> <span class=\"n\">port_out</span><span class=\"o\">=</span><span class=\"mi\">5556</span><span class=\"p\">)</span> <span class=\"c1\"># 创建客户端对象\n</span> <span class=\"c1\"># 注意:可以参考API,查看其它参数的设置\n</span> <span class=\"c1\"># 127.0.0.1 表示本机IP,也可以用localhost\n</span> <span class=\"k\">except</span><span class=\"p\">:</span>\n <span class=\"k\">raise</span> <span class=\"nb\">Exception</span><span class=\"p\">(</span><span class=\"s\">\"cannot create BertClient\"</span><span class=\"p\">)</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">close_bert</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">sentence_embedding</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">text</span><span class=\"p\">):</span>\n <span class=\"s\">'''对输入文本进行embedding\n Args:\n text: str, 输入文本\n Returns:\n text_vector: float, 返回一个列表,包含text的embedding编码值\n '''</span>\n <span class=\"n\">text_vector</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">encode</span><span class=\"p\">([</span><span class=\"n\">text</span><span class=\"p\">])[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"k\">return</span> <span class=\"n\">text_vector</span> <span class=\"c1\"># 获取输出结果\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">caculate_similarity</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">vec_1</span><span class=\"p\">,</span> <span class=\"n\">vec_2</span><span class=\"p\">):</span>\n <span class=\"s\">'''根据两个语句的vector,计算它们的相似性\n Args:\n vec_1: float, 语句1的vector\n vec_2: float, 语句2的vector\n Returns:\n sim_value: float, 返回相似性的计算值\n '''</span>\n <span class=\"c1\"># 根据cosine的计算公式\n</span> <span class=\"n\">v1</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_1</span><span class=\"p\">)</span>\n <span class=\"n\">v2</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_2</span><span class=\"p\">)</span>\n <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">v1</span> <span class=\"o\">*</span> <span class=\"n\">v2</span><span class=\"p\">.</span><span class=\"n\">T</span><span class=\"p\">)</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v1</span><span class=\"p\">)</span> <span class=\"o\">*</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v2</span><span class=\"p\">)</span>\n <span class=\"n\">cosine</span> <span class=\"o\">=</span> <span class=\"n\">a</span> <span class=\"o\">/</span> <span class=\"n\">b</span>\n <span class=\"k\">return</span> <span class=\"n\">cosine</span>\n\n\n<span class=\"k\">if</span> <span class=\"n\">__name__</span> <span class=\"o\">==</span> <span class=\"s\">\"__main__\"</span><span class=\"p\">:</span>\n <span class=\"c1\"># 创建bert对象\n</span> <span class=\"n\">bert</span> <span class=\"o\">=</span> <span class=\"n\">BertModel</span><span class=\"p\">()</span>\n <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n <span class=\"c1\"># --- 输入语句 ----\n</span> <span class=\"n\">input_a</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句1: '</span><span class=\"p\">)</span>\n\n <span class=\"k\">if</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"N\"</span> <span class=\"ow\">or</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"n\"</span><span class=\"p\">:</span>\n <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">close_bert</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span> <span class=\"k\">break</span>\n\n <span class=\"n\">input_b</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句2: '</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># --- 对输入语句进行embedding ---\n</span>\n <span class=\"n\">a_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_a</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'a_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">a_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"n\">b_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_b</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'b_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 计算两个语句的相似性\n</span> <span class=\"n\">cos</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">caculate_similarity</span><span class=\"p\">(</span><span class=\"n\">a_vec</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'cosine value : '</span><span class=\"p\">,</span> <span class=\"n\">cos</span><span class=\"p\">)</span>\n\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'</span><span class=\"se\">\\n\\n</span><span class=\"s\">'</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\n</span> <span class=\"k\">if</span> <span class=\"n\">cos</span> <span class=\"o\">></span> <span class=\"mf\">0.85</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"2个语句的含义相似\"</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"不相似\"</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<p>在使用 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 连接 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 时,你需要确保 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 使用的模型和 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\n\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>请输入语句1: \n请输入语句2: \n</code></pre></div></div>\n\n<p>两句输入好确认后,得到如下形式的结果:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>a_vec shape : (768,)\nb_vec shape : (768,)\ncosine value : 0.8691698561422959\n</code></pre></div></div>\n\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\n\n<h3 id=\"三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</h3>\n\n<p>论文地址:<a href=\"https://arxiv.org/abs/1810.04805\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\n\n<h4 id=\"1bert-的优势是很明显的\">1、BERT 的优势是很明显的</h4>\n\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\n\n<blockquote>\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\n</blockquote>\n\n<h5 id=\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\n\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\n\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\n\n<h5 id=\"12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</h5>\n\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\n\n<h5 id=\"13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</h5>\n\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\n\n<h4 id=\"2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</h4>\n\n<h5 id=\"21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\n\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\n\n<h5 id=\"22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</h5>\n\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\n\n<h5 id=\"23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\n\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\n\n<h5 id=\"24需要的算力高\">2.4、需要的算力高</h5>\n\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\n\n<h5 id=\"25需要的模型大\">2.5、需要的模型大</h5>\n\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\n\n<h3 id=\"四一些关于-bert-的问题\">四、一些关于 BERT 的问题</h3>\n\n<h4 id=\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\n\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\n\n<h4 id=\"2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\n\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://arxiv.org/abs/1810.04805</li>\n <li>https://github.com/google-research/bert</li>\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"NLP":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\n \t<meta name=\"description\" content=\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\t\t\n\t<time datetime=\"2022-12-24T15:08:01+00:00\" class=\"by-line\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一自然语言处理领域近年的发展关键节点\" id=\"markdown-toc-一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</a> <ul>\n <li><a href=\"#1从理性主义到经验主义\" id=\"markdown-toc-1从理性主义到经验主义\">1、从理性主义到经验主义</a></li>\n <li><a href=\"#2经验主义的早期还不是深度学习\" id=\"markdown-toc-2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</a></li>\n <li><a href=\"#3撇开特征让机器囫囵吞枣地学吧\" id=\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\n <li><a href=\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\" id=\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\n <li><a href=\"#5自监督学习法让我们省去人工标注\" id=\"markdown-toc-5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</a></li>\n <li><a href=\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\" id=\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\n <li><a href=\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\" id=\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\n <li><a href=\"#8数据和算力有了还不够\" id=\"markdown-toc-8数据和算力有了还不够\">8、数据和算力有了,还不够</a></li>\n </ul>\n </li>\n <li><a href=\"#二学术里程碑几篇重量级论文\" id=\"markdown-toc-二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</a> <ul>\n <li><a href=\"#1提出-transformer-的attention-is-all-you-need2017\" id=\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\n <li><a href=\"#2elmo-deep-contextualized-word-representations\" id=\"markdown-toc-2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</a></li>\n <li><a href=\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\" id=\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\n <li><a href=\"#4gpt-3-language-models-are-few-shot-learners2020\" id=\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\n <li><a href=\"#其他的重量级论文\" id=\"markdown-toc-其他的重量级论文\">其他的重量级论文</a></li>\n </ul>\n </li>\n <li><a href=\"#三行业里程碑\" id=\"markdown-toc-三行业里程碑\">三、行业里程碑</a></li>\n <li><a href=\"#四成本\" id=\"markdown-toc-四成本\">四、成本</a></li>\n <li><a href=\"#五业内应用\" id=\"markdown-toc-五业内应用\">五、业内应用</a></li>\n <li><a href=\"#五行业内哪些人的言论值得我们日常重点关注\" id=\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</h3>\n\n<p><img src=\"/img/src/2022-12-17-ai-bert-1-1.jpg\" alt=\"image\" /></p>\n\n<h4 id=\"1从理性主义到经验主义\">1、从理性主义到经验主义</h4>\n\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\n\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\n\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\n\n<h4 id=\"2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</h4>\n\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\n\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\n\n<h4 id=\"3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\n\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\n\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\n\n<h4 id=\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\n\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\n\n<h4 id=\"5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</h4>\n\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\n\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\n\n<h4 id=\"6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\n\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\n\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\n\n<h4 id=\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\n\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\n\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\n\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\n\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\n\n<h4 id=\"8数据和算力有了还不够\">8、数据和算力有了,还不够</h4>\n\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\n\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\n\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\n\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\n\n<h3 id=\"二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</h3>\n\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\n\n<h4 id=\"1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\n\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\n\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\n\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\n\n<h4 id=\"2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</h4>\n\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\n\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\n\n<h4 id=\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\n\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\n\n<ul>\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\n <li>在预训练中引入自监督学习任务。</li>\n</ul>\n\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\n\n<h4 id=\"4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\n\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\n\n<h4 id=\"其他的重量级论文\">其他的重量级论文</h4>\n\n<ul>\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\n <li>……</li>\n</ul>\n\n<h3 id=\"三行业里程碑\">三、行业里程碑</h3>\n\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\n\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\n\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\n\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\n\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\n\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\n\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\n\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\n\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\n\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\n\n<h3 id=\"四成本\">四、成本</h3>\n\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\n\n<ul>\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\n</ul>\n\n<h3 id=\"五业内应用\">五、业内应用</h3>\n\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\n\n<ul>\n <li>虚拟客服(可以乱真的)</li>\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\n <li>智能翻译</li>\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\n <li>AI 广告公司:替代传统广告公司</li>\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\n</ul>\n\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\n\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\n\n<ul>\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-7.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-2.jpg\" alt=\"image\" /></p>\n\n<ul>\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-8.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年年底,西湖心辰公司发布「<a href=\"https://www.heyfriday.cn/\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-1.png\" alt=\"image\" /></p>\n\n<h3 id=\"五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</h3>\n\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\n\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\n\n<blockquote>\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\n</blockquote>\n\n<blockquote>\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\n</blockquote>\n\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\n\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\n\n<blockquote>\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\n</blockquote>\n\n<blockquote>\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\n</blockquote>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://beta.openai.com/docs/models</li>\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\n <li>https://hub.baai.ac.cn/view/21726</li>\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\n <li>https://www.sohu.com/a/615541698_121255906</li>\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"自然语言处理":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\n \t<meta name=\"description\" content=\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\t\t\n\t<time datetime=\"2022-12-24T15:08:01+00:00\" class=\"by-line\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一自然语言处理领域近年的发展关键节点\" id=\"markdown-toc-一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</a> <ul>\n <li><a href=\"#1从理性主义到经验主义\" id=\"markdown-toc-1从理性主义到经验主义\">1、从理性主义到经验主义</a></li>\n <li><a href=\"#2经验主义的早期还不是深度学习\" id=\"markdown-toc-2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</a></li>\n <li><a href=\"#3撇开特征让机器囫囵吞枣地学吧\" id=\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\n <li><a href=\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\" id=\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\n <li><a href=\"#5自监督学习法让我们省去人工标注\" id=\"markdown-toc-5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</a></li>\n <li><a href=\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\" id=\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\n <li><a href=\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\" id=\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\n <li><a href=\"#8数据和算力有了还不够\" id=\"markdown-toc-8数据和算力有了还不够\">8、数据和算力有了,还不够</a></li>\n </ul>\n </li>\n <li><a href=\"#二学术里程碑几篇重量级论文\" id=\"markdown-toc-二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</a> <ul>\n <li><a href=\"#1提出-transformer-的attention-is-all-you-need2017\" id=\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\n <li><a href=\"#2elmo-deep-contextualized-word-representations\" id=\"markdown-toc-2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</a></li>\n <li><a href=\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\" id=\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\n <li><a href=\"#4gpt-3-language-models-are-few-shot-learners2020\" id=\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\n <li><a href=\"#其他的重量级论文\" id=\"markdown-toc-其他的重量级论文\">其他的重量级论文</a></li>\n </ul>\n </li>\n <li><a href=\"#三行业里程碑\" id=\"markdown-toc-三行业里程碑\">三、行业里程碑</a></li>\n <li><a href=\"#四成本\" id=\"markdown-toc-四成本\">四、成本</a></li>\n <li><a href=\"#五业内应用\" id=\"markdown-toc-五业内应用\">五、业内应用</a></li>\n <li><a href=\"#五行业内哪些人的言论值得我们日常重点关注\" id=\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</h3>\n\n<p><img src=\"/img/src/2022-12-17-ai-bert-1-1.jpg\" alt=\"image\" /></p>\n\n<h4 id=\"1从理性主义到经验主义\">1、从理性主义到经验主义</h4>\n\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\n\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\n\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\n\n<h4 id=\"2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</h4>\n\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\n\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\n\n<h4 id=\"3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\n\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\n\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\n\n<h4 id=\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\n\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\n\n<h4 id=\"5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</h4>\n\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\n\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\n\n<h4 id=\"6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\n\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\n\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\n\n<h4 id=\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\n\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\n\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\n\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\n\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\n\n<h4 id=\"8数据和算力有了还不够\">8、数据和算力有了,还不够</h4>\n\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\n\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\n\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\n\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\n\n<h3 id=\"二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</h3>\n\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\n\n<h4 id=\"1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\n\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\n\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\n\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\n\n<h4 id=\"2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</h4>\n\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\n\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\n\n<h4 id=\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\n\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\n\n<ul>\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\n <li>在预训练中引入自监督学习任务。</li>\n</ul>\n\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\n\n<h4 id=\"4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\n\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\n\n<h4 id=\"其他的重量级论文\">其他的重量级论文</h4>\n\n<ul>\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\n <li>……</li>\n</ul>\n\n<h3 id=\"三行业里程碑\">三、行业里程碑</h3>\n\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\n\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\n\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\n\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\n\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\n\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\n\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\n\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\n\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\n\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\n\n<h3 id=\"四成本\">四、成本</h3>\n\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\n\n<ul>\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\n</ul>\n\n<h3 id=\"五业内应用\">五、业内应用</h3>\n\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\n\n<ul>\n <li>虚拟客服(可以乱真的)</li>\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\n <li>智能翻译</li>\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\n <li>AI 广告公司:替代传统广告公司</li>\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\n</ul>\n\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\n\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\n\n<ul>\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-7.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-2.jpg\" alt=\"image\" /></p>\n\n<ul>\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-8.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年年底,西湖心辰公司发布「<a href=\"https://www.heyfriday.cn/\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-1.png\" alt=\"image\" /></p>\n\n<h3 id=\"五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</h3>\n\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\n\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\n\n<blockquote>\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\n</blockquote>\n\n<blockquote>\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\n</blockquote>\n\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\n\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\n\n<blockquote>\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\n</blockquote>\n\n<blockquote>\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\n</blockquote>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://beta.openai.com/docs/models</li>\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\n <li>https://hub.baai.ac.cn/view/21726</li>\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\n <li>https://www.sohu.com/a/615541698_121255906</li>\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"]},"posts":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\n \t<meta name=\"description\" content=\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\t\t\n\t<time datetime=\"2022-12-24T15:08:01+00:00\" class=\"by-line\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一自然语言处理领域近年的发展关键节点\" id=\"markdown-toc-一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</a> <ul>\n <li><a href=\"#1从理性主义到经验主义\" id=\"markdown-toc-1从理性主义到经验主义\">1、从理性主义到经验主义</a></li>\n <li><a href=\"#2经验主义的早期还不是深度学习\" id=\"markdown-toc-2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</a></li>\n <li><a href=\"#3撇开特征让机器囫囵吞枣地学吧\" id=\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\n <li><a href=\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\" id=\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\n <li><a href=\"#5自监督学习法让我们省去人工标注\" id=\"markdown-toc-5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</a></li>\n <li><a href=\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\" id=\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\n <li><a href=\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\" id=\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\n <li><a href=\"#8数据和算力有了还不够\" id=\"markdown-toc-8数据和算力有了还不够\">8、数据和算力有了,还不够</a></li>\n </ul>\n </li>\n <li><a href=\"#二学术里程碑几篇重量级论文\" id=\"markdown-toc-二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</a> <ul>\n <li><a href=\"#1提出-transformer-的attention-is-all-you-need2017\" id=\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\n <li><a href=\"#2elmo-deep-contextualized-word-representations\" id=\"markdown-toc-2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</a></li>\n <li><a href=\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\" id=\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\n <li><a href=\"#4gpt-3-language-models-are-few-shot-learners2020\" id=\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\n <li><a href=\"#其他的重量级论文\" id=\"markdown-toc-其他的重量级论文\">其他的重量级论文</a></li>\n </ul>\n </li>\n <li><a href=\"#三行业里程碑\" id=\"markdown-toc-三行业里程碑\">三、行业里程碑</a></li>\n <li><a href=\"#四成本\" id=\"markdown-toc-四成本\">四、成本</a></li>\n <li><a href=\"#五业内应用\" id=\"markdown-toc-五业内应用\">五、业内应用</a></li>\n <li><a href=\"#五行业内哪些人的言论值得我们日常重点关注\" id=\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</h3>\n\n<p><img src=\"/img/src/2022-12-17-ai-bert-1-1.jpg\" alt=\"image\" /></p>\n\n<h4 id=\"1从理性主义到经验主义\">1、从理性主义到经验主义</h4>\n\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\n\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\n\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\n\n<h4 id=\"2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</h4>\n\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\n\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\n\n<h4 id=\"3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\n\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\n\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\n\n<h4 id=\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\n\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\n\n<h4 id=\"5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</h4>\n\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\n\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\n\n<h4 id=\"6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\n\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\n\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\n\n<h4 id=\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\n\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\n\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\n\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\n\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\n\n<h4 id=\"8数据和算力有了还不够\">8、数据和算力有了,还不够</h4>\n\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\n\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\n\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\n\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\n\n<h3 id=\"二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</h3>\n\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\n\n<h4 id=\"1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\n\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\n\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\n\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\n\n<h4 id=\"2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</h4>\n\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\n\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\n\n<h4 id=\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\n\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\n\n<ul>\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\n <li>在预训练中引入自监督学习任务。</li>\n</ul>\n\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\n\n<h4 id=\"4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\n\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\n\n<h4 id=\"其他的重量级论文\">其他的重量级论文</h4>\n\n<ul>\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\n <li>……</li>\n</ul>\n\n<h3 id=\"三行业里程碑\">三、行业里程碑</h3>\n\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\n\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\n\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\n\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\n\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\n\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\n\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\n\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\n\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\n\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\n\n<h3 id=\"四成本\">四、成本</h3>\n\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\n\n<ul>\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\n</ul>\n\n<h3 id=\"五业内应用\">五、业内应用</h3>\n\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\n\n<ul>\n <li>虚拟客服(可以乱真的)</li>\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\n <li>智能翻译</li>\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\n <li>AI 广告公司:替代传统广告公司</li>\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\n</ul>\n\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\n\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\n\n<ul>\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-7.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-2.jpg\" alt=\"image\" /></p>\n\n<ul>\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-8.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年年底,西湖心辰公司发布「<a href=\"https://www.heyfriday.cn/\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-1.png\" alt=\"image\" /></p>\n\n<h3 id=\"五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</h3>\n\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\n\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\n\n<blockquote>\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\n</blockquote>\n\n<blockquote>\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\n</blockquote>\n\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\n\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\n\n<blockquote>\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\n</blockquote>\n\n<blockquote>\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\n</blockquote>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://beta.openai.com/docs/models</li>\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\n <li>https://hub.baai.ac.cn/view/21726</li>\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\n <li>https://www.sohu.com/a/615541698_121255906</li>\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\n \t<meta name=\"description\" content=\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\t\t\n\t<time datetime=\"2022-12-17T15:08:01+00:00\" class=\"by-line\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一关于-bert-的一些背景\" id=\"markdown-toc-一关于-bert-的一些背景\">一、关于 BERT 的一些背景</a></li>\n <li><a href=\"#二开始一个-bert-的动手小试验\" id=\"markdown-toc-二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</a> <ul>\n <li><a href=\"#1安装-anaconda-来为部署-bert-做环境准备\" id=\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\n <li><a href=\"#2安装-bert-所需要的各种依赖\" id=\"markdown-toc-2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</a></li>\n <li><a href=\"#3下载一个预训练pre-train过的-bert-模型\" id=\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\n <li><a href=\"#5启动-bert-服务端\" id=\"markdown-toc-5启动-bert-服务端\">5、启动 BERT 服务端</a></li>\n <li><a href=\"#6在-pycharm-中使用-conda-的环境\" id=\"markdown-toc-6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</a></li>\n <li><a href=\"#7编写程序实现-bert-客户端\" id=\"markdown-toc-7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</a></li>\n </ul>\n </li>\n <li><a href=\"#三bert-模型的优劣势及其原因\" id=\"markdown-toc-三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</a> <ul>\n <li><a href=\"#1bert-的优势是很明显的\" id=\"markdown-toc-1bert-的优势是很明显的\">1、BERT 的优势是很明显的</a> <ul>\n <li><a href=\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\" id=\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\n <li><a href=\"#12识别并专注于较重要的部分进行文本处理\" id=\"markdown-toc-12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\n <li><a href=\"#13快速构建针对具体任务的-nlp-系统\" id=\"markdown-toc-13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\n </ul>\n </li>\n <li><a href=\"#2bert-模型的劣势及其原因\" id=\"markdown-toc-2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</a> <ul>\n <li><a href=\"#21随机挖-mask-的完形填空题是有隐患的\" id=\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\n <li><a href=\"#22nsp-任务有必要吗\" id=\"markdown-toc-22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</a></li>\n <li><a href=\"#23针对两个或以上词组成的连续词的词义被丢失\" id=\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\n <li><a href=\"#24需要的算力高\" id=\"markdown-toc-24需要的算力高\">2.4、需要的算力高</a></li>\n <li><a href=\"#25需要的模型大\" id=\"markdown-toc-25需要的模型大\">2.5、需要的模型大</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四一些关于-bert-的问题\" id=\"markdown-toc-四一些关于-bert-的问题\">四、一些关于 BERT 的问题</a> <ul>\n <li><a href=\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\" id=\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\n <li><a href=\"#2为什么-bert-可以比-rnn-更好地并行化\" id=\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一关于-bert-的一些背景\">一、关于 BERT 的一些背景</h3>\n\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\n\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\n\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\n\n<h3 id=\"二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</h3>\n\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\n\n<h4 id=\"1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\n\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\n\n<p>更新 conda 到最新版本:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda update <span class=\"nt\">-n</span> base conda\n</code></pre></div></div>\n\n<p>使用 Python 3.7 创建一个新的环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda create <span class=\"nt\">-n</span> py37 <span class=\"nv\">python</span><span class=\"o\">=</span>3.7\n</code></pre></div></div>\n\n<p>激活这个新环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda activate py37\n</code></pre></div></div>\n\n<p>验证正在使用的是正确版本的 Python</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>python <span class=\"nt\">--version</span>\n</code></pre></div></div>\n\n<p>另外你可能还会用到的 conda 命令有:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\nconda deactivate py37\n\n<span class=\"c\"># 查看 conda 当前安装的所有库</span>\nconda list\n</code></pre></div></div>\n\n<h4 id=\"2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda <span class=\"nb\">install </span><span class=\"nv\">tensorflow</span><span class=\"o\">==</span>1.14.0\n</code></pre></div></div>\n\n<p>验证 tensorflow 是否安装正确:</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">import</span> <span class=\"nn\">tensorflow</span> <span class=\"k\">as</span> <span class=\"n\">tf</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">tf</span><span class=\"p\">.</span><span class=\"n\">__version__</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<h4 id=\"3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\n\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\n\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\n\n<ul>\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n</ul>\n\n<p>4、安装 BERT 的服务端和客户端</p>\n\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\n\n<p>在你激活的环境里,安装 <code class=\"language-plaintext highlighter-rouge\">bert-as-service</code>:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 安装服务端和客户端</span>\n<span class=\"c\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\nconda <span class=\"nb\">install </span>bert-serving-server bert-serving-client \n验证 bert-as-service 是否安装成功\nbert-serving-start <span class=\"nt\">-h</span>\n</code></pre></div></div>\n\n<h4 id=\"5启动-bert-服务端\">5、启动 BERT 服务端</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 命令行下启动BERT服务</span>\n<span class=\"c\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\nbert-serving-start <span class=\"nt\">-model_dir</span> /模型/的/绝对/路径 <span class=\"nt\">-num_worker</span><span class=\"o\">=</span>4\n</code></pre></div></div>\n\n<h4 id=\"6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</h4>\n\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\n\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\"language-plaintext highlighter-rouge\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\n\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\n\n<h4 id=\"7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</h4>\n\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">from</span> <span class=\"nn\">bert_serving.client</span> <span class=\"kn\">import</span> <span class=\"n\">BertClient</span>\n<span class=\"kn\">import</span> <span class=\"nn\">numpy</span> <span class=\"k\">as</span> <span class=\"n\">np</span>\n\n<span class=\"c1\"># 定义类\n</span><span class=\"k\">class</span> <span class=\"nc\">BertModel</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"nf\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span> <span class=\"o\">=</span> <span class=\"n\">BertClient</span><span class=\"p\">(</span><span class=\"n\">ip</span><span class=\"o\">=</span><span class=\"s\">'127.0.0.1'</span><span class=\"p\">,</span> <span class=\"n\">port</span><span class=\"o\">=</span><span class=\"mi\">5555</span><span class=\"p\">,</span> <span class=\"n\">port_out</span><span class=\"o\">=</span><span class=\"mi\">5556</span><span class=\"p\">)</span> <span class=\"c1\"># 创建客户端对象\n</span> <span class=\"c1\"># 注意:可以参考API,查看其它参数的设置\n</span> <span class=\"c1\"># 127.0.0.1 表示本机IP,也可以用localhost\n</span> <span class=\"k\">except</span><span class=\"p\">:</span>\n <span class=\"k\">raise</span> <span class=\"nb\">Exception</span><span class=\"p\">(</span><span class=\"s\">\"cannot create BertClient\"</span><span class=\"p\">)</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">close_bert</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">sentence_embedding</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">text</span><span class=\"p\">):</span>\n <span class=\"s\">'''对输入文本进行embedding\n Args:\n text: str, 输入文本\n Returns:\n text_vector: float, 返回一个列表,包含text的embedding编码值\n '''</span>\n <span class=\"n\">text_vector</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">encode</span><span class=\"p\">([</span><span class=\"n\">text</span><span class=\"p\">])[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"k\">return</span> <span class=\"n\">text_vector</span> <span class=\"c1\"># 获取输出结果\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">caculate_similarity</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">vec_1</span><span class=\"p\">,</span> <span class=\"n\">vec_2</span><span class=\"p\">):</span>\n <span class=\"s\">'''根据两个语句的vector,计算它们的相似性\n Args:\n vec_1: float, 语句1的vector\n vec_2: float, 语句2的vector\n Returns:\n sim_value: float, 返回相似性的计算值\n '''</span>\n <span class=\"c1\"># 根据cosine的计算公式\n</span> <span class=\"n\">v1</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_1</span><span class=\"p\">)</span>\n <span class=\"n\">v2</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_2</span><span class=\"p\">)</span>\n <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">v1</span> <span class=\"o\">*</span> <span class=\"n\">v2</span><span class=\"p\">.</span><span class=\"n\">T</span><span class=\"p\">)</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v1</span><span class=\"p\">)</span> <span class=\"o\">*</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v2</span><span class=\"p\">)</span>\n <span class=\"n\">cosine</span> <span class=\"o\">=</span> <span class=\"n\">a</span> <span class=\"o\">/</span> <span class=\"n\">b</span>\n <span class=\"k\">return</span> <span class=\"n\">cosine</span>\n\n\n<span class=\"k\">if</span> <span class=\"n\">__name__</span> <span class=\"o\">==</span> <span class=\"s\">\"__main__\"</span><span class=\"p\">:</span>\n <span class=\"c1\"># 创建bert对象\n</span> <span class=\"n\">bert</span> <span class=\"o\">=</span> <span class=\"n\">BertModel</span><span class=\"p\">()</span>\n <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n <span class=\"c1\"># --- 输入语句 ----\n</span> <span class=\"n\">input_a</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句1: '</span><span class=\"p\">)</span>\n\n <span class=\"k\">if</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"N\"</span> <span class=\"ow\">or</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"n\"</span><span class=\"p\">:</span>\n <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">close_bert</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span> <span class=\"k\">break</span>\n\n <span class=\"n\">input_b</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句2: '</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># --- 对输入语句进行embedding ---\n</span>\n <span class=\"n\">a_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_a</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'a_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">a_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"n\">b_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_b</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'b_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 计算两个语句的相似性\n</span> <span class=\"n\">cos</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">caculate_similarity</span><span class=\"p\">(</span><span class=\"n\">a_vec</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'cosine value : '</span><span class=\"p\">,</span> <span class=\"n\">cos</span><span class=\"p\">)</span>\n\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'</span><span class=\"se\">\\n\\n</span><span class=\"s\">'</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\n</span> <span class=\"k\">if</span> <span class=\"n\">cos</span> <span class=\"o\">></span> <span class=\"mf\">0.85</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"2个语句的含义相似\"</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"不相似\"</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<p>在使用 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 连接 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 时,你需要确保 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 使用的模型和 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\n\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>请输入语句1: \n请输入语句2: \n</code></pre></div></div>\n\n<p>两句输入好确认后,得到如下形式的结果:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>a_vec shape : (768,)\nb_vec shape : (768,)\ncosine value : 0.8691698561422959\n</code></pre></div></div>\n\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\n\n<h3 id=\"三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</h3>\n\n<p>论文地址:<a href=\"https://arxiv.org/abs/1810.04805\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\n\n<h4 id=\"1bert-的优势是很明显的\">1、BERT 的优势是很明显的</h4>\n\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\n\n<blockquote>\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\n</blockquote>\n\n<h5 id=\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\n\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\n\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\n\n<h5 id=\"12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</h5>\n\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\n\n<h5 id=\"13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</h5>\n\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\n\n<h4 id=\"2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</h4>\n\n<h5 id=\"21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\n\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\n\n<h5 id=\"22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</h5>\n\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\n\n<h5 id=\"23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\n\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\n\n<h5 id=\"24需要的算力高\">2.4、需要的算力高</h5>\n\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\n\n<h5 id=\"25需要的模型大\">2.5、需要的模型大</h5>\n\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\n\n<h3 id=\"四一些关于-bert-的问题\">四、一些关于 BERT 的问题</h3>\n\n<h4 id=\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\n\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\n\n<h4 id=\"2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\n\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://arxiv.org/abs/1810.04805</li>\n <li>https://github.com/google-research/bert</li>\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\n \t<meta name=\"description\" content=\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\t\t\n\t<time datetime=\"2022-12-11T15:59:57+00:00\" class=\"by-line\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2022-12-11-wechat-chatgpt-3.png\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\n\n<h3 id=\"stepbystep\">Step by step</h3>\n\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\n\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\n\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\n\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\n\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\n\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\n\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">chatGPTAccountPool</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"na\">email</span><span class=\"pi\">:</span> <span class=\"s\"><your email></span>\n <span class=\"na\">password</span><span class=\"pi\">:</span> <span class=\"s\"><your password></span>\n<span class=\"c1\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\n<span class=\"na\">chatPrivateTiggerKeyword</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">\"</span>\n</code></pre></div></div>\n\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker pull holegots/wechat-chatgpt:latest。\n</code></pre></div></div>\n\n<p>第八步,启动 wechat-chatgpt:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker run <span class=\"nt\">-d</span> <span class=\"nt\">--name</span> wechat-chatgpt <span class=\"nt\">-v</span> <span class=\"si\">$(</span><span class=\"nb\">pwd</span><span class=\"si\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\n</code></pre></div></div>\n\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\"language-plaintext highlighter-rouge\">npm install && poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-1.png\" alt=\"image\" style=\"width:100%\" /></th>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-2.png\" alt=\"image\" style=\"width:100%\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\n\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker logs <span class=\"nt\">-f</span> wechat-chatgpt\n</code></pre></div></div>\n\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\n\n<p>如果要停止运行,用如下命令:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker stop wechat-chatgpt\n</code></pre></div></div>\n\n<h3 id=\"参考\">参考</h3>\n\n<p>1、<a href=\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>不要船开远了,就忘了为什么启航</title>\n \t<meta name=\"description\" content=\"2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>不要船开远了,就忘了为什么启航</h2>\t\t\n\t<time datetime=\"2022-08-11T15:53:57+00:00\" class=\"by-line\">11 Aug 2022, 杭州 | 作者 麦克船长 | 总计 3223 字</time>\n\t<div class=\"content\">\n\t\t<h3 id=\"写在前面\">写在前面</h3>\n<p>偶然翻到 2020.06.11 刚来到阿里时写的一篇内容(我是 2020 年的 6 月 4 日我入职阿里巴巴集团),是有关于来阿里的期待、对这家公司的一些粗浅初步的理解。此时再翻来看看,最大的感触就是,提醒自己勿忘初心。</p>\n\n<p>在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:</p>\n\n<ul>\n <li>很多人是带着梦想来阿里的,那么我的梦想是什么呢?</li>\n <li>最喜欢新六脉的哪句话?为什么?</li>\n <li>关于阿里企业价值观:为什么要接受这套价值观?</li>\n <li>价值观的本质意义(极度务实视角)是什么?</li>\n <li>Landing 的 SOP</li>\n <li>问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-1.png\" alt=\"image\" /></p>\n\n<p>注:2020 年平安夜 · 百年湖畔 87 期合影</p>\n\n<h3 id=\"很多人是带着梦想来阿里的那么我的梦想是什么呢\">很多人是带着梦想来阿里的,那么我的梦想是什么呢?</h3>\n\n<p>Christensen 在《创新者的窘境》中提到:每一次技术更迭,都需要破坏性创新,而破坏性创新在前一次技术更迭的胜出者内部是很难生长出来的。阿里诞生以来,不断地创造第二增长曲线:阿里巴巴、淘宝、支付宝、天猫、阿里云、钉钉 …… 这让我非常好奇。其中很多产品穿越多个时间周期,期间不断创造内生二次曲线。</p>\n\n<p>但是阿里也一样错失了很多,微信、美团、拼多多、抖/快…… 等等很多产品诞生在了其他公司,还有某些产品在不断的科技更迭中自身生长出了第二曲线。</p>\n\n<p>因此我来阿里的梦想也非常明确:<strong>参与或创造一次(甚至多次)第二曲线,可以是新产品,也可以是原有产品内生的。在这个过程中获得个人成长、个人价值。</strong></p>\n\n<p>一直以来,我有三个最想实现或得到的东西:LOVE、CREATION、FREEDOM。随着生活与工作的前行,对这三者的理解,在不断加深。在这个问题里,我想应该是讨论”CREATION”。</p>\n\n<p>CREATION 上,我的梦想的范式,大概是从自己中学时代就确立了,在某一次人类社会变革浪潮中,扮演有一定权重的角色。这里面有几个变量:<strong>什么领域(F)的变革;什么规模(S)的变革;多大的权重(W);什么角色(R)。</strong></p>\n\n<p>F 这个变量,我在中学及大学时代逐渐明确,是以相对普适的产品形式输出结果并对社会变革产生积极作用。后来越来越明确为科技与商业结合的领域。</p>\n\n<p>S、W 这两个变量,自然是越大越好。因此我会希望能够构建尽可能大的机会,或者参与到尽可能大的机会中。R 希望是有强烈 Ownership 的身份。</p>\n\n<p>因此过去几年我选择了创业。创业就像冲浪,你抓住一次浪并完成漂亮的动作,就是一次不算失败的创业。但是如果一个浪没抓住,你去追它是没意义的,而应该等待下一个浪。我认为在未来 5~10 年内难以出现规模能大到令我足够兴奋的科技浪潮。大浪潮中属于创业者的大机会很多,而中小浪潮的大机会基本只属于大平台,那么为了在壮年期做获得我的 CREATION,我选择了加入阿里这样的大平台。</p>\n\n<p>在最后做决定以及初来阿里的那个人生转折点,作为老阿里人的曲洋老师对我说的一句话,深深地鼓励了我,他说:”带着创业气质,把这里当你的舞台折腾!”</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-2.png\" alt=\"image\" /></p>\n\n<p>注:2020 年双十一 · 淘宝 KO</p>\n\n<h3 id=\"最喜欢新六脉的哪句话为什么\">最喜欢新六脉的哪句话?为什么?</h3>\n\n<p>最喜欢的是“因为信任所以简单”。</p>\n\n<p>我一直认为人最重要的两个元特质是”真实”和“谦逊”,由”真实”可以塑造自我(对内)、构建信任(对外),后者可以带来清晰的边界,继而实现人与人之间高效的互动(这种互动包括各种人际关系在内,如婚姻、合伙、共事、合作等等)。</p>\n\n<p><strong>事物(虚实皆可)呈现在人的认知中,会得到三方面的投影:facts、opinion、feeling。如果我们足够真实,当我们需要把这三方面呈现给他人时,双方就能顺畅建立信任。信任的结果,就对应到这三方面:彼此之间建立共识(facts)、求同存异(opinion)、尊重感受(feeling),这就是”简单”。</strong></p>\n\n<p>另外一句是鼓励自己勇于绽放的一条:「此次此刻,非我莫属」。</p>\n\n<p>激情、自信、积极…… 通常行为统一表现为“勇于绽放自己”,绽放有表达(语言)与投身(行为)两种表现形式。更进一步推进就是”此次此刻,非我莫属”的阿里价值观。</p>\n\n<p>低调、稳重、谦逊,其实与“此次此刻,非我莫属“,并不矛盾。这点是我来到阿里后,发现自己在过去这些年的创业中已经不知不觉改变了,从 Introvert 逐渐变成了 Extrovert 的人,而且从曾经 social 中消耗能量,逐渐变为我现在可以感知到获得能量。这种变化,是我最近来阿里才确认发生的,此前因为自己创业者的身份没有察觉这种变化的发生。</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-3.png\" alt=\"image\" /></p>\n\n<p>注:2021 年秋 · 径山之行</p>\n\n<h3 id=\"关于阿里企业价值观为什么要接受这套价值观\">关于阿里企业价值观:为什么要接受这套价值观?</h3>\n\n<p>马老师和老逍都提到这个:我们是寻找同路人,而不是教育别人。这其实非常明晰地解释了为什么阿里要构建一个毛细血管网络一样的政委体系。基于这种用人理念,政委体系不敢说是最优解,但一定是优解(而且是否有更优解的论证没有意义)。</p>\n\n<p>对于个人,我的理解是要做两件事:<strong>1)构建自己的价值观体系(初始化);2)寻找价值观契合的公司(做匹配)。这两点里,没有任何地方提到”你要改变价值观为了契合你所在的公司”。</strong></p>\n\n<p>而企业价值观呢,其实可以分两部分看待:普世价值观、独特价值观。前者因为是普世的,所以到了哪个公司这种价值观都对,这点老逍也提了,比如“客户第一”。后者是个性化的,但不存在孰高孰低,就像一个人内向还是外向,你不能说哪个是错的。</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-4.png\" alt=\"image\" /></p>\n\n<p>注:2021 年双十一 · 天天特卖团队</p>\n\n<h3 id=\"价值观的本质意义极度务实视角是什么\">价值观的本质意义(极度务实视角)是什么?</h3>\n\n<p><strong>在充分考虑价值观适配使命、愿景基础上,价值观本身的意义,在和风细雨时(即企业价值观与其他价值判断相 match 时),是看不到的。但在暴风骤雨时(即企业价值观与其他价值判断相冲突时),就能显示其实实在在的作用了。</strong>我认为包括三类,前两个是阿里整体视角,第三个是阿里内部:</p>\n\n<ul>\n <li>经济体内,阿里与其他生态位的冲突或损益关系,如曾经的美蘑口一役。</li>\n <li>经济体内,其他的生态位之间的冲突或损益关系,如曾经的十月围城。</li>\n <li>阿里人的行为价值判断,如最近的钉钉代考事件、过往的各类廉政事件。</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-5.png\" alt=\"image\" /></p>\n\n<p>注:2021 年冬 · 淘宝天猫合并前合影</p>\n\n<h3 id=\"landing的sop\">Landing 的 SOP</h3>\n\n<p>大家都说 landing 充满挑战,马老师其实给出了 landing 的 SOP 三部曲:<strong>一起打过仗、一起创过新、一起度过难。三个经历都 close 才算 smooth landing。</strong></p>\n\n<p>集团人才策略层面、HR 实操层面、Leader 层面、,对于新人 landing 能做到什么程度的保障,其实每个新人感受到的不尽相同:</p>\n\n<ul>\n <li>集团层面,始终是在构建更好的新人 landing 环境的,这符合自身价值,这能打下很好的底子。</li>\n <li>实操层面,包括面试阶段对候选人的价值观判断、预期管理,面试及入职后公司文化及人才体系的事实呈现、内化吸收和长期解惑。</li>\n <li>Leader 层面,这是新人体感最强烈的部分,也是最重要的部分。尽管拥抱变化,但首先 Leader 需要给出尽可能最全面的考虑,其次是对候选人的预期管理。好的 Leader 会给候选人提供合理的着陆点、多个降落伞、缓冲垫,完成 smooth landing。</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-6.png\" alt=\"image\" /></p>\n\n<p>注:2021 年夏 · 出差厦漳泉</p>\n\n<h3 id=\"问问自己来到阿里如果初期我可能需要做一点改变那会是什么\">问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</h3>\n\n<p>曾经个人的激情与动力,常来自于“增长”。常说<strong>高增长掩盖一切</strong>,所以未来在阿里如果不能如创业般快速获得反馈得到积极结果,并且大平台中必然要接受大量关联方共同参与项目而导致的效率降低,因此我要逐渐改变自己,重新适应这种环境下的激情与动力获得方式。</p>\n\n<p>另一方面,作为创业公司的负责人,工作中鲜有因为内部原因而无法推进的事情,但是扮演肩部或腰部角色时,需要接受头部决策的一定程度不可控,这是我需要作出的适应与改变。关于这一点,我在几个月前就已经在做预期管理和心态调整,我认为以创业者的强适应性,这可能并不会是问题,但是我习惯于保持谨慎的乐观来面对自己。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的 Jekyll 快速教程</title>\n \t<meta name=\"description\" content=\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的 Jekyll 快速教程</h2>\t\t\n\t<time datetime=\"2021-12-23T19:43:02+00:00\" class=\"by-line\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\n\n<h3 id=\"part-1基本特点\">Part 1、基本特点</h3>\n\n<h4 id=\"一基本语法\">一、基本语法</h4>\n\n<ul>\n <li>变量:用双大括号表示变量 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code></li>\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\n <li>支持循环与分支结构:比如 <code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 和 <code class=\"language-plaintext highlighter-rouge\">if-elsif-else-endif</code> :可以使用 <code class=\"language-plaintext highlighter-rouge\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\n</ul>\n\n<h4 id=\"二典型-jekyll-项目结构及重要文件介绍\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\n\n<h5 id=\"1配置文件-_configyml\">1、配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code></h5>\n\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\"language-plaintext highlighter-rouge\">encoding: utf-8</code> 这一条。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Site settings</span>\n<span class=\"na\">encoding</span><span class=\"pi\">:</span> <span class=\"s\">utf-8</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">麦克船长的技术、产品与商业博客</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\"</span>\n<span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptain.com\"</span>\n<span class=\"na\">author</span><span class=\"pi\">:</span>\n <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Your</span><span class=\"nv\"> </span><span class=\"s\">Name\"</span>\n <span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptian.com\"</span>\n</code></pre></div></div>\n\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Markdown and highlighter</span>\n<span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Plugins</span>\n<span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-paginate</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-sitemap</span>\n</code></pre></div></div>\n\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Build settings</span>\n<span class=\"na\">baseurl</span><span class=\"pi\">:</span> <span class=\"c1\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\n<span class=\"na\">source</span><span class=\"pi\">:</span> <span class=\"s\">.</span>\n<span class=\"na\">destination</span><span class=\"pi\">:</span> <span class=\"s\">./_site</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:title</span>\n<span class=\"na\">paginate</span><span class=\"pi\">:</span> <span class=\"m\">20</span>\n<span class=\"na\">paginate_path</span><span class=\"pi\">:</span> <span class=\"s\">/page:num/</span>\n<span class=\"na\">collections</span><span class=\"pi\">:</span>\n <span class=\"na\">posts</span><span class=\"pi\">:</span>\n <span class=\"na\">output</span><span class=\"pi\">:</span> <span class=\"no\">true</span>\n <span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:year/:month/:day/:title/</span>\n <span class=\"na\">directory</span><span class=\"pi\">:</span> <span class=\"s\">_posts</span>\n</code></pre></div></div>\n\n<h5 id=\"2布局文件_layouts-目录下的文件规则\">2、布局文件:<code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的文件规则</h5>\n\n<p>Jekyll 的 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\"language-plaintext highlighter-rouge\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\"language-plaintext highlighter-rouge\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\n\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\"language-plaintext highlighter-rouge\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\n\n<h5 id=\"3页面文件及其头部\">3、页面文件及其头部</h5>\n\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">page</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/categories/</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">Categories</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>每个页面文件的头部都会有layout,并与 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的某个文件对应。</p>\n\n<h3 id=\"part-2jekyll-中的全局变量\">Part 2、Jekyll 中的全局变量</h3>\n\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:包含当前页面或文章的内容。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:包含有关网站的所有标签的信息。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\n</ul>\n\n<p>这些变量可以在模板中使用,比如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><h1></span>{{ page.title }}<span class=\"nt\"></h1></span>\n<span class=\"nt\"><p></span>{{ site.description }}<span class=\"nt\"></p></span>\n<span class=\"nt\"><ul></span>\n {{ for category in site.categories %}\n <span class=\"nt\"><li></span>{{ category }}<span class=\"nt\"></li></span>\n {{ endfor %}\n<span class=\"nt\"></ul></span> \n</code></pre></div></div>\n\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">my_custom_variable</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Hello</span><span class=\"nv\"> </span><span class=\"s\">World\"</span>\n</code></pre></div></div>\n\n<p>然后就可以在模板文件中使用 <code class=\"language-plaintext highlighter-rouge\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\n\n<h4 id=\"一site变量\">一、site变量</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\n\n<h5 id=\"1sitecategories\">1、<code class=\"language-plaintext highlighter-rouge\">site.categories</code></h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\"language-plaintext highlighter-rouge\">first</code> 就是 <code class=\"language-plaintext highlighter-rouge\">category</code> 的名字,如下使用:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>\n</code></pre></div></div>\n\n<h5 id=\"2sitepages\">2、site.pages</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\"language-plaintext highlighter-rouge\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3其他常用属性\">3、其他常用属性</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site.title</code>:是网站的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.related_posts</code>:相关文章的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.data</code>:从 <code class=\"language-plaintext highlighter-rouge\">_data</code> 目录加载的数据。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.static_files</code>:静态文件的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.collections</code>:自定义集合的列表。</li>\n</ul>\n\n<h4 id=\"二page-变量\">二、<code class=\"language-plaintext highlighter-rouge\">page</code> 变量</h4>\n\n<p>在 Jekyll 中,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量属性包括:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">layout</code>:表示页面使用的布局模板的名称。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">title</code>:表示页面的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">date</code>:表示页面的发布日期。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">categories</code>:表示页面所属的分类列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:表示页面所属的标签列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\n</ul>\n\n<p>在模板中,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.属性名 }}</code> 的方式来访问 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.title }}</code>。此外,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量还有其他属性,如 <code class=\"language-plaintext highlighter-rouge\">permalink</code>、<code class=\"language-plaintext highlighter-rouge\">excerpt</code>、<code class=\"language-plaintext highlighter-rouge\">url</code> 等,可以根据需要调用。</p>\n\n<h3 id=\"part-3控制结构\">Part 3、控制结构</h3>\n\n<h4 id=\"1if-else-分支结构\">1、<code class=\"language-plaintext highlighter-rouge\">if-else</code> 分支结构</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ if tmp_var == \"type1\" %}\n{{ elsif tmp_var == \"type2\" %}\n{{ elsif tmp_var == \"type3\" %}\n{{ elsif tmp_var == \"type4\" %}\n{{ else tmp_var == \"type5\" %}\n{{ endif %}\n</code></pre></div></div>\n\n<h4 id=\"2for-endfor-循环结构\">2、<code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 循环结构</h4>\n\n<p>不带条件判断的 <code class=\"language-plaintext highlighter-rouge\">for</code> 循环如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for post in paginator.posts %}\n\t<span class=\"c\"><!-- Your other sentences --></span>\n{{ endfor %}\n</code></pre></div></div>\n\n<p>带条件循环的 <code class=\"language-plaintext highlighter-rouge\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages | where: \"dir\", \"categories\" %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3jekyll-支持的其他结构包括\">3、Jekyll 支持的其他结构包括:</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">capture</code> 用于捕获输出的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">cycle</code> 用于循环一组字符串的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">include</code> 用于包含其他文件的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">while</code> 用于在满足指定条件时执行代码的结构。</li>\n</ul>\n\n<h3 id=\"参考\">参考:</h3>\n\n<p>1、<a href=\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\n2、<a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\n \t<meta name=\"description\" content=\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\t\t\n\t<time datetime=\"2021-12-21T15:53:57+00:00\" class=\"by-line\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#写在前面\" id=\"markdown-toc-写在前面\">写在前面</a></li>\n <li><a href=\"#1github上的准备\" id=\"markdown-toc-1github上的准备\">1、GitHub 上的准备</a></li>\n <li><a href=\"#2了解ruby和jekyll\" id=\"markdown-toc-2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</a></li>\n <li><a href=\"#3了解gem\" id=\"markdown-toc-3了解gem\">3、了解 Gem</a></li>\n <li><a href=\"#4安装homebrew\" id=\"markdown-toc-4安装homebrew\">4、安装 Homebrew</a></li>\n <li><a href=\"#5用homebrew安装ruby\" id=\"markdown-toc-5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</a></li>\n <li><a href=\"#6安装jekyll和bundler\" id=\"markdown-toc-6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</a></li>\n <li><a href=\"#7使用bundle管理包依赖关系\" id=\"markdown-toc-7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</a></li>\n <li><a href=\"#8本地启动一下看看\" id=\"markdown-toc-8本地启动一下看看\">8、本地启动一下看看</a></li>\n <li><a href=\"#9用jekyll创建一个项目\" id=\"markdown-toc-9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</a></li>\n <li><a href=\"#10修改gemfile文件\" id=\"markdown-toc-10修改gemfile文件\">10、修改 Gemfile 文件</a></li>\n <li><a href=\"#11配置githubpages\" id=\"markdown-toc-11配置githubpages\">11、配置 Github Pages</a></li>\n <li><a href=\"#12配置一个jekylltheme\" id=\"markdown-toc-12配置一个jekylltheme\">12、配置一个 Jekyll Theme</a></li>\n <li><a href=\"#13设置自定义域名\" id=\"markdown-toc-13设置自定义域名\">13、设置自定义域名</a></li>\n <li><a href=\"#14用-rouge-实现代码高亮\" id=\"markdown-toc-14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</a></li>\n <li><a href=\"#15一些扩展问题\" id=\"markdown-toc-15一些扩展问题\">15、一些扩展问题</a> <ul>\n <li><a href=\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\" id=\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\n <li><a href=\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\" id=\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\n <li><a href=\"#q3如何为每篇文章添加一个目录\" id=\"markdown-toc-q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</a></li>\n <li><a href=\"#q4如何在-jekyll-中支持-katex\" id=\"markdown-toc-q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\n <li><a href=\"#在-githubio-上\" id=\"markdown-toc-在-githubio-上\">在 GitHub.io 上</a></li>\n <li><a href=\"#如果不在-githubio-上则还需要额外工作\" id=\"markdown-toc-如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\n <li><a href=\"#使用示例\" id=\"markdown-toc-使用示例\">使用示例</a></li>\n </ul>\n </li>\n <li><a href=\"#q5jekyll-中如何支持-graphviz-\" id=\"markdown-toc-q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\n <li><a href=\"#q6如何显示--或者--\" id=\"markdown-toc-q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</a></li>\n </ul>\n </li>\n <li><a href=\"#参考\" id=\"markdown-toc-参考\">参考</a></li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\n\n<ul>\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\n</ul>\n\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\n\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\n\n<h3 id=\"1github上的准备\">1、GitHub 上的准备</h3>\n\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git clone https://github.com/username/username.github.io\n</code></pre></div></div>\n\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git add <span class=\"nt\">--all</span>\n<span class=\"nv\">$ </span>git commit <span class=\"nt\">-m</span> <span class=\"s2\">\"Initial commit\"</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</h3>\n\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\n\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\n\n<h3 id=\"3了解gem\">3、了解 Gem</h3>\n\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\n\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\ngem sources -l\n</code></pre></div></div>\n\n<h3 id=\"4安装homebrew\">4、安装 Homebrew</h3>\n\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>/bin/bash <span class=\"nt\">-c</span> <span class=\"s2\">\"</span><span class=\"si\">$(</span>curl <span class=\"nt\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\"si\">)</span><span class=\"s2\">\"</span>\n</code></pre></div></div>\n\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\n\n<h3 id=\"5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</h3>\n\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>brew <span class=\"nb\">install </span>chruby ruby-install xz\n</code></pre></div></div>\n\n<p>安装 Ruby 的最新版本:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby-install ruby\n</code></pre></div></div>\n\n<p>这时候提示如下问题:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"o\">>>></span> Updating ruby versions ...\n<span class=\"o\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\"se\">\\</span>\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\n<span class=\"o\">!!!</span> Failed to download ruby versions!\n</code></pre></div></div>\n\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ echo \"185.199.111.133 raw.githubusercontent.com\" >> /etc/hosts\n</code></pre></div></div>\n\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/chruby.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/auto.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"chruby ruby-3.1.2\"</span> <span class=\"o\">>></span> ~/.zshrc <span class=\"c\"># run 'chruby' to see actual version</span>\n</code></pre></div></div>\n\n<p>再看下 Ruby 版本对不对:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby <span class=\"nt\">-v</span>\n</code></pre></div></div>\n\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\n\n<h3 id=\"6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"nb\">install </span>jekyll bundler\n</code></pre></div></div>\n\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\n\n<h3 id=\"7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</h3>\n\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">source</span> <span class=\"s1\">'https://rubygems.org'</span>\ngem <span class=\"s1\">'nokogiri'</span>\ngem <span class=\"s1\">'rack'</span>, <span class=\"s1\">'~> 2.2.4'</span>\ngem <span class=\"s1\">'rspec'</span>\ngem <span class=\"s1\">'jekyll'</span>\n</code></pre></div></div>\n\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ git add Gemfile Gemfile.lock\n</code></pre></div></div>\n\n<h3 id=\"8本地启动一下看看\">8、本地启动一下看看</h3>\n\n<p>先用 bundle 如下命令来启动:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: none\n Source: /Users/captain/Workspace/poechant.github.io\n Destination: /Users/captain/Workspace/poechant.github.io/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n done in 0.014 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\n Server address: http://127.0.0.1:4000\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\" alt=\"image\" /></p>\n\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git pull <span class=\"nt\">--no-rebase</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll new CaptainMikeBlog\n<span class=\"nv\">$ </span><span class=\"nb\">cd </span>CaptainMikeBlog\n<span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n Jekyll Feed: Generating feed for posts\n done in 0.365 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\n Server address: http://127.0.0.1:4000/\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\" alt=\"image\" /></p>\n\n<h3 id=\"10修改gemfile文件\">10、修改 Gemfile 文件</h3>\n\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"s2\">\"github-pages\"</span>, <span class=\"s2\">\"~> GITHUB-PAGES-VERSION\"</span>, group: :jekyll_plugins\n</code></pre></div></div>\n\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ bundle install\n</code></pre></div></div>\n\n<p>再本地启动服务器测试:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>得到如下提示:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\nPrepending <span class=\"sb\">`</span>bundle <span class=\"nb\">exec</span><span class=\"sb\">`</span> to your <span class=\"nb\">command </span>may solve this. <span class=\"o\">(</span>Gem::LoadError<span class=\"o\">)</span>\n</code></pre></div></div>\n\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle add webrick\n<span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\n\n<h3 id=\"11配置githubpages\">11、配置 Github Pages</h3>\n\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>baseurl: \"\"\nurl: \"http://your-username.github.io\"\n</code></pre></div></div>\n\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\n\n<h3 id=\"12配置一个jekylltheme\">12、配置一个 Jekyll Theme</h3>\n\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_includes\n_layouts\n_sass\ncss\njs\nimg\n404.markdown\nindex.html\n</code></pre></div></div>\n\n<p>不同主题会有所不同,这里只列个大概。</p>\n\n<h3 id=\"13设置自定义域名\">13、设置自定义域名</h3>\n\n<p>添加四条 A 记录,记录值如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>185.199.108.153\n185.199.109.153\n185.199.110.153\n185.199.111.153\n</code></pre></div></div>\n\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\n\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\n\n<h3 id=\"14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</h3>\n\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem install kramdom rouge\n</code></pre></div></div>\n\n<p>然后配置 _config.yml 文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>然后用 rouge 创建 syntax.css 文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>rougify style github <span class=\"o\">></span> css/syntax.css\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">_include/head.html</code> 文件中添加:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span> <span class=\"na\">href=</span><span class=\"s\">\"/css/syntax.css\"</span> <span class=\"nt\">/></span>\n</code></pre></div></div>\n\n<h3 id=\"15一些扩展问题\">15、一些扩展问题</h3>\n\n<h4 id=\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\n\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">这是一篇文章</span>\n<span class=\"na\">excerpt</span><span class=\"pi\">:</span> <span class=\"s\">这是文章的摘要</span>\n<span class=\"nn\">---</span>\n\n这是文章的正文内容\n</code></pre></div></div>\n\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><ul></span>\n {% for post in paginator.posts %}\n <span class=\"nt\"><li></span>\n <span class=\"nt\"><h2><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{{ post.url }}\"</span><span class=\"nt\">></span>{{ post.title }}<span class=\"nt\"></a></h2></span>\n <span class=\"nt\"><p></span>{{ post.excerpt }}<span class=\"nt\"></p></span>\n <span class=\"nt\"></li></span>\n {% endfor %}\n<span class=\"nt\"></ul></span>\n</code></pre></div></div>\n\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\n\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\n\n<h4 id=\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\n\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\"language-plaintext highlighter-rouge\">_layouts</code>目录下创建一个<code class=\"language-plaintext highlighter-rouge\">category.html</code>文件:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>---\nlayout: default\n---\n\n<span class=\"nt\"><div</span> <span class=\"na\">class=</span><span class=\"s\">\"container\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><br></span>\n {% if site.categories[page.category] %}\n {% for post in site.categories[page.category] %}\n <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{% if site.baseurl == \"</span><span class=\"err\">/\"</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">}}{%</span> <span class=\"na\">else</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">|</span> <span class=\"na\">prepend:</span> <span class=\"na\">site.baseurl</span> <span class=\"err\">}}{%</span> <span class=\"na\">endif</span> <span class=\"err\">%}\"</span><span class=\"nt\">></span>\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\n <span class=\"nt\"></a></span>\n {% endfor %}\n {% else %}\n <span class=\"nt\"><br></span>\n <span class=\"nt\"><p></span>No posts for this category. If you have something in mind, check <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"/write\"</span><span class=\"nt\">></span>Write For Us<span class=\"nt\"></a></span>page.<span class=\"nt\"></p></span>\n {% endif %}\n<span class=\"nt\"></div></span>\n</code></pre></div></div>\n\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\"language-plaintext highlighter-rouge\">_config.yml</code>文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">include</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"s1\">'</span><span class=\"s\">_categories'</span><span class=\"pi\">]</span>\n</code></pre></div></div>\n\n<p>在根目录创建一个<code class=\"language-plaintext highlighter-rouge\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\"language-plaintext highlighter-rouge\">ai.html</code>如下:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">category</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">人工智能</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s\">This is the description.</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/category/ai</span>\n<span class=\"na\">category</span><span class=\"pi\">:</span> <span class=\"s\">ai</span>\n<span class=\"na\">category_type</span><span class=\"pi\">:</span> <span class=\"s\">tech</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>那么之后每次创建文件时,在头部写<code class=\"language-plaintext highlighter-rouge\">category</code>一定要与这些<code class=\"language-plaintext highlighter-rouge\">categories</code>中的<code class=\"language-plaintext highlighter-rouge\">html</code>文件对应起来。</p>\n\n<h4 id=\"q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</h4>\n\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">*</span> TOC\n{:toc}\n</code></pre></div></div>\n\n<h4 id=\"q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\n\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\n\n<h5 id=\"在-githubio-上\">在 GitHub.io 上</h5>\n\n<p>先修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code>:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">math_engine</span><span class=\"pi\">:</span> <span class=\"s\">katex</span>\n</code></pre></div></div>\n\n<p>然后修改 <code class=\"language-plaintext highlighter-rouge\">_includes/head.html</code> 文件,在 <code class=\"language-plaintext highlighter-rouge\"><head></code> 与 <code class=\"language-plaintext highlighter-rouge\"></head></code> 中间:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"><!--KaTeX--></span>\n <span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span>\n <span class=\"na\">href=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script></span>\n <span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">addEventListener</span><span class=\"p\">(</span><span class=\"dl\">\"</span><span class=\"s2\">DOMContentLoaded</span><span class=\"dl\">\"</span><span class=\"p\">,</span> <span class=\"kd\">function</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"nx\">renderMathInElement</span><span class=\"p\">(</span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">body</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n <span class=\"c1\">// ...options...</span>\n <span class=\"p\">});</span>\n <span class=\"p\">});</span>\n <span class=\"nt\"></script></span>\n</code></pre></div></div>\n\n<h5 id=\"如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</h5>\n\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem <span class=\"nb\">install </span>kramdom-math-katex\n\ngem <span class=\"nb\">install </span>katex\ngem <span class=\"nb\">install </span>execjs\n\ngem <span class=\"nb\">install </span>therubyracer\ngem <span class=\"nb\">install </span>therubyrhino\ngem <span class=\"nb\">install </span>duktape\n</code></pre></div></div>\n\n<h5 id=\"使用示例\">使用示例</h5>\n\n<p>以如下方式输入输入如下内容:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}\n$$ \\sum_{i=1}^{n} a_i $$\n{% endraw %}\n</code></pre></div></div>\n\n<p>就会得到一个数学公式:</p>\n\n\\[\\sum_{i=1}^{n} a_i\\]\n\n<h4 id=\"q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\n\n<p>这要依赖 <code class=\"language-plaintext highlighter-rouge\">jekyll-graphviz-dot</code>,修改 <code class=\"language-plaintext highlighter-rouge\">Gemfile</code> 增加一句:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>group :jekyll_plugins <span class=\"k\">do\n </span>gem <span class=\"s2\">\"jekyll-graphviz-dot\"</span>\nend\n</code></pre></div></div>\n\n<p>再修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 配置文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-graphviz</span>\n</code></pre></div></div>\n\n<p>再在本地安装 graphviz,可以通过 <code class=\"language-plaintext highlighter-rouge\">conda install graphviz</code> 或者 <code class=\"language-plaintext highlighter-rouge\">brew install graphviz</code>。然后 <code class=\"language-plaintext highlighter-rouge\">bundle install</code> 再 <code class=\"language-plaintext highlighter-rouge\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\n\n<pre><code class=\"language-graphviz\">{% graph some graph title %}\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n{% endgraph %}\n</code></pre>\n\n<p>如果看到如下效果,就说明你都配置成功了:</p>\n\n<div class=\"graphviz-wrapper\">\n\n<!-- Generated by graphviz version 2.43.0 (0)\n -->\n<!-- Title: G Pages: 1 -->\n<svg role=\"img\" aria-label=\"some graph title\" width=\"89pt\" height=\"188pt\" viewBox=\"0.00 0.00 89.00 188.00\">\n<title>some graph title</title>\n<desc>\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n</desc>\n\n<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n<title>G</title>\n<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-184 85,-184 85,4 -4,4\" />\n<!-- a -->\n<g id=\"node1\" class=\"node\">\n<title>a</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-162\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">a</text>\n</g>\n<!-- b -->\n<g id=\"node2\" class=\"node\">\n<title>b</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">b</text>\n</g>\n<!-- a->b -->\n<g id=\"edge1\" class=\"edge\">\n<title>a->b</title>\n<path fill=\"none\" stroke=\"black\" d=\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\" />\n</g>\n<!-- c -->\n<g id=\"node3\" class=\"node\">\n<title>c</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-18\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">c</text>\n</g>\n<!-- b->c -->\n<g id=\"edge2\" class=\"edge\">\n<title>b->c</title>\n<path fill=\"none\" stroke=\"black\" d=\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\" />\n</g>\n<!-- c->a -->\n<g id=\"edge3\" class=\"edge\">\n<title>c->a</title>\n<path fill=\"none\" stroke=\"black\" d=\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\" />\n</g>\n</g>\n</svg>\n</div>\n\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\n\n<h4 id=\"q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</h4>\n\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 呢?方法如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}{%{% endraw %} raw %}\n{% raw %}{%{% endraw %} endraw %}\n</code></pre></div></div>\n\n<p>如上,就是用 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 把 <code class=\"language-plaintext highlighter-rouge\">{%</code> 包起来,但是 <code class=\"language-plaintext highlighter-rouge\">%}</code> 不用包。应该讲的很清楚了吧。</p>\n\n<h3 id=\"参考\">参考</h3>\n\n<ol>\n <li><a href=\"https://bundler.io\">https://bundler.io</a></li>\n <li><a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></li>\n <li><a href=\"https://zhuanlan.zhihu.com/p/87225594\">https://zhuanlan.zhihu.com/p/87225594</a></li>\n <li><a href=\"https://chat.openai.com/chat\">https://chat.openai.com/chat</a></li>\n <li><a href=\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\n <li><a href=\"https://github.com/dyutibarma/monochrome\">https://github.com/dyutibarma/monochrome</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\n <li><a href=\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\n <li><a href=\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</title>\n \t<meta name=\"description\" content=\"阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</h2>\t\t\n\t<time datetime=\"2021-11-11T19:59:43+00:00\" class=\"by-line\">11 Nov 2021, 杭州 | 作者 麦克船长 | 总计 917 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2021-11-11-captain-tttm-1.jpg\" alt=\"imagee\" /></p>\n\n<h3 id=\"天天特卖团队理念\">天天特卖团队理念</h3>\n\n<h4 id=\"特卖合伙人\">特卖合伙人</h4>\n\n<p>以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。特卖核心管理团队每 Q 会进行一次班子建设通晒。</p>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-9.jpg\" alt=\"imagee\" /></p>\n\n<h4 id=\"如何理解协作\">如何理解协作?</h4>\n\n<p>从长时间线来看,我们是为了不断积累信用,像一张信用卡一样,不断获得别人愿意支持我们的更大额度。不要事情结果还可以,而我们却没有积累到信用。互联网本质也是现代工业。而现代工业,一是社会分工,二是社会协作。想取得现代工业项目的结果,就要有更大的人才包容度、环境包容度。工作的结果就是在妥协与博弈中取得的,这是和光同尘的本质,也是现代工业复杂系统拿到结果的本质。只有这样我们才能让越来越多的人追随我们一起 do something,这种追随不一定只有上下级才是,而是愿意并且相信和我们能到达更远的地方。这背后的信用,要我们一步一个脚印地去积累,对他人给予的信任要保持敬畏、如履薄冰、懂得感恩。对每一段阶段性或长或短结束的合作,都要表达感谢。</p>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-8.jpg\" alt=\"imagee\" /></p>\n\n<h4 id=\"如何看待同学的优势及短板\">如何看待同学的优势及短板?</h4>\n\n<ul>\n <li>优势:讲优势有两个可能的目的,要么组织会在未来任务分配上重点考虑发挥该同学优势的事情,要告诉 TA,要激励 TA,是 TA 前行的自信来源之一。要么是对于同学也把握不准的特点,我们明确告诉 TA 这是你被我欣赏的优点。</li>\n <li>短板:什么是要讲的短板?未来一段时间,最期待你补足提升的。一旦这方面显著进步,就会向上迈进很大一步,甚至可以突破自己当下成长的瓶颈。要花多少篇幅讲?要比优势,有更大篇幅去讲。讲完就结束了么?对这个短板,一定要表达态度,也一定要对是否有方法、什么方法来补足短板要和同学沟通。</li>\n <li>无论是优势,还是短板,要说到点儿上,不要说片儿汤话。要让同学们能够引起思考、启发的。</li>\n</ul>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-10.jpg\" alt=\"imagee\" /></p>\n\n<h3 id=\"天天特卖期待你的加入\">天天特卖期待你的加入!</h3>\n\n<p>新天天特卖缘起于「手淘下沉市场战役)」,于 2021 年初上线,以「极致性价比货源、裸价直降、全网比价、买贵必赔」打造手淘极致价格敏感人群的购物阵地。目前天天特卖团队有行业运营、用户运营、数据策略、整合营销、直播运营、内容运营等岗位,有兴趣的同学可以钉钉随时找我,期待你的加入!</p>\n\n<h4 id=\"欢迎添加我的微信sinosuperman-推荐自荐--\">欢迎添加我的微信:sinosuperman 推荐、自荐 ^ ^</h4>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-11.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-2.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-3.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-4.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-5.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-6.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-7.jpg\" alt=\"imagee\" /></p>\n\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的阿里一年香(入职阿里一周年)</title>\n \t<meta name=\"description\" content=\"本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的阿里一年香(入职阿里一周年)</h2>\t\t\n\t<time datetime=\"2021-06-04T15:42:43+00:00\" class=\"by-line\">04 Jun 2021, 杭州 | 作者 麦克船长 | 总计 304 字</time>\n\t<div class=\"content\">\n\t\t<p>To 钟超</p>\n\n<p>1 周年快乐!很开心我们有这样一段共事的机会,虽开始时有些许波折,但随着进一步相处,我们很快能做到彼此欣赏、英雄相惜、默契配合,也特别感谢你对我的信任和支持,这是一切共事的基础。你强大的自驱力、脑力、对新事物的理解学习能力,都是最近几手新人里比较突出的。特别钦佩于你的执着和初性,对一件事认定后,迸发出的强大战斗力和决心。今天特卖这个新业务需要扎下根基,还真的需要一些舍我其谁的胆魄和更为犀利的突破,我也相信「新特卖」能成为你在阿里又一代表作,我希望我们的团队能为之骄傲和自豪,我们能不负公司所托,真正在下沉市场这场硬仗上有所建树,井取得令我们自己感到骄傲的突破,一起加油。</p>\n\n<p>From 麦克船长的主管</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>担任淘宝产品总负责人的双十一,是怎样的体验?</title>\n \t<meta name=\"description\" content=\"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>担任淘宝产品总负责人的双十一,是怎样的体验?</h2>\t\t\n\t<time datetime=\"2020-11-11T15:59:43+00:00\" class=\"by-line\">11 Nov 2020, 杭州 | 作者 麦克船长 | 总计 138 字</time>\n\t<div class=\"content\">\n\t\t<p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\n\n<p><img src=\"/img/src/2020-11-11-captain-double-eleven-1.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-2.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-3.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-4.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-5.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-6.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-7.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-8.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-9.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-10.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-11.jpg\" alt=\"image\" /></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>疫后怎么做餐饮品牌?三叉戟模式或成标配</title>\n \t<meta name=\"description\" content=\"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>疫后怎么做餐饮品牌?三叉戟模式或成标配</h2>\t\t\n\t<time datetime=\"2020-04-14T16:42:43+00:00\" class=\"by-line\">14 Apr 2020, 杭州 | 作者 麦克船长 | 总计 1845 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><img src=\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\n\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\n\n<ul>\n <li>外卖</li>\n <li>做饭</li>\n <li>速食</li>\n</ul>\n\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\n\n<h3 id=\"标品化外卖业务\">标品化外卖业务</h3>\n\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\n\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\n\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\n\n<ul>\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\n</ul>\n\n<h3 id=\"社区生鲜前置仓\">社区生鲜前置仓</h3>\n\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\n\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\n\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\n\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\n\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\n\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\n\n<ul>\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\n</ul>\n\n<h3 id=\"线上预包装食品\">线上预包装食品</h3>\n\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\n\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\n\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\n\n<ul>\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\n</ul>\n\n<h3 id=\"线下重构新餐饮时代到来\">线下重构,新餐饮时代到来</h3>\n\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\n\n<ul>\n <li>用「标品化外卖」覆盖外卖场景</li>\n <li>用「生鲜前置仓」覆盖做饭场景</li>\n <li>用「预包装食品」覆盖速食场景</li>\n</ul>\n\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\n\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\n\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>延迟满足,才有自由</title>\n \t<meta name=\"description\" content=\"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>延迟满足,才有自由</h2>\t\t\n\t<time datetime=\"2020-04-11T06:18:03+00:00\" class=\"by-line\">11 Apr 2020, 杭州 | 作者 麦克船长 | 总计 4478 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\n\n<h3 id=\"1儿童时期的延迟满足\">1、儿童时期的延迟满足</h3>\n\n<h4 id=\"棉花糖实验\">棉花糖实验</h4>\n\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\n\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\n\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\n\n<p>先讲两个我自己的例子吧。</p>\n\n<h4 id=\"黑加仑零食\">黑加仑零食</h4>\n\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\n\n<h4 id=\"暑假作业\">暑假作业</h4>\n\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\n\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\n\n<h4 id=\"延迟满足的背后是意志力自控力\">延迟满足的背后,是意志力/自控力</h4>\n\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\n\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\n\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\n\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\n\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\n\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\n\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\n\n<h3 id=\"2快乐理论挑战延迟满足\">2、快乐理论,挑战延迟满足</h3>\n\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\n\n<h4 id=\"烂苹果\">烂苹果</h4>\n\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\n\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\n\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\n\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\n\n<h4 id=\"快乐理论\">快乐理论</h4>\n\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\n\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\n\n<p>但这个理论角度对吗?</p>\n\n<h3 id=\"3我们应该在哪个范畴内讨论延迟满足\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\n\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\n\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\n\n<h4 id=\"仅有正反馈刺激的奶头乐\">仅有正反馈刺激的奶头乐</h4>\n\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\n\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\n\n<h4 id=\"延迟满足重在顺序而非时间\">延迟满足重在顺序,而非时间</h4>\n\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\n\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\n\n<h3 id=\"4常见的延迟满足与即时满足\">4、常见的延迟满足与即时满足</h3>\n\n<h4 id=\"初阶对手vs浅度延迟满足\">初阶对手 vs 浅度延迟满足</h4>\n\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\n\n<ul>\n <li>\n <p>睡懒觉</p>\n </li>\n <li>\n <p>过量饮食</p>\n </li>\n <li>\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\n </li>\n <li>\n <p>冲动情绪</p>\n </li>\n <li>\n <p>炫耀(粗俗说就是装B)</p>\n </li>\n</ul>\n\n<p>……</p>\n\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\n\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\n\n<h4 id=\"中阶对手vs中度延迟满足\">中阶对手 vs 中度延迟满足</h4>\n\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\n\n<ul>\n <li>\n <p>在家边看电视/视频,边学习/工作</p>\n </li>\n <li>\n <p>依赖二手知识,而怠于一手知识</p>\n </li>\n <li>\n <p>刷知乎、行业资讯 APP</p>\n </li>\n</ul>\n\n<p>……</p>\n\n<p>例子会有很多,我们仅稍微说下这三个。</p>\n\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\n\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\n\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\n\n<h4 id=\"高阶对手vs深度延迟满足\">高阶对手 vs 深度延迟满足</h4>\n\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\n\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\n\n<p>我这里就说一个影响很大的:</p>\n\n<p>* 急于行动</p>\n\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\n\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\n\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\n\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\n\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\n\n<h3 id=\"5延迟满足的驻留时长\">5、延迟满足的驻留时长</h3>\n\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\n\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\n\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\n\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\n\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\n\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\n\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\n\n<h3 id=\"后记\">后记</h3>\n\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\n\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>未来人工智能就是要:让普通人过上现在富豪们的生活</title>\n \t<meta name=\"description\" content=\"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>未来人工智能就是要:让普通人过上现在富豪们的生活</h2>\t\t\n\t<time datetime=\"2017-02-23T18:23:33+00:00\" class=\"by-line\">23 Feb 2017, 北京 | 作者 麦克船长 | 总计 627 字</time>\n\t<div class=\"content\">\n\t\t<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\n\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\n\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\n\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\n\n<h4 id=\"但是我没有钱呢\">但是我没有钱呢?</h4>\n\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\n\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\n\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\n\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\n\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\n\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\n\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>我们是应该「断舍离」还是「念念不忘,必有回响」</title>\n \t<meta name=\"description\" content=\"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>我们是应该「断舍离」还是「念念不忘,必有回响」</h2>\t\t\n\t<time datetime=\"2017-01-31T20:59:21+00:00\" class=\"by-line\">31 Jan 2017, 北京 | 作者 麦克船长 | 总计 2577 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"引子\">引子</h3>\n\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\n\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\n\n<p>「嗨,我说老朱,讲讲你啊!」</p>\n\n<p>「我就不提啦。」</p>\n\n<p>「为什么啊?说说,说说!」</p>\n\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\n\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\n\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\n\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\n\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\n\n<blockquote>\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\n</blockquote>\n\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\n\n<h3 id=\"断舍离\">断舍离</h3>\n\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\n\n<p><img src=\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\" alt=\"image\" /></p>\n\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\n\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\n\n<h3 id=\"念念不忘必有回响\">念念不忘,必有回响</h3>\n\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\n\n<p><img src=\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\" alt=\"image\" /></p>\n\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\n\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\n\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\n\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\n\n<h3 id=\"用正负反馈来判断何时何为\">用「正负反馈」来判断何时何为?</h3>\n\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\n\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\n\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\n\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\n\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\n\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\n\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\n\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\n\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\n\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\n\n<h3 id=\"后记\">后记</h3>\n\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\n\n<p>然后,我们一起等待,那声回响。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</h2>\t\t\n\t<time datetime=\"2012-08-04T17:58:17+00:00\" class=\"by-line\">04 Aug 2012, 广州 | 作者 麦克船长 | 总计 5236 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一rtmfpserver-线程的启动和等待\" id=\"markdown-toc-一rtmfpserver-线程的启动和等待\">一、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的启动和等待</a> <ul>\n <li><a href=\"#1pocothread\" id=\"markdown-toc-1pocothread\">1、<code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code></a></li>\n <li><a href=\"#2封装一个可运行线程的类\" id=\"markdown-toc-2封装一个可运行线程的类\">2、封装一个可运行线程的类</a></li>\n <li><a href=\"#3启动-rtmfpserver-线程\" id=\"markdown-toc-3启动-rtmfpserver-线程\">3、启动 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</a></li>\n <li><a href=\"#4rtmfpserver-线程等待\" id=\"markdown-toc-4rtmfpserver-线程等待\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程等待</a></li>\n </ul>\n </li>\n <li><a href=\"#二rtmfpmanager-对-rtmfpserver-的影响\" id=\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\">二、<code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的影响</a></li>\n</ul>\n\n<h3 id=\"一rtmfpserver-线程的启动和等待\">一、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的启动和等待</h3>\n\n<h4 id=\"1pocothread\">1、<code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code></h4>\n\n<p>Cumulus 大量使用了 <code class=\"language-plaintext highlighter-rouge\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">PoechantRunnable</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Runnable</span> <span class=\"p\">{</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"c1\">// your codes</span>\n <span class=\"p\">}</span>\n<span class=\"p\">};</span>\n \n<span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">PoechantRunnable</span> <span class=\"n\">runnable</span><span class=\"p\">;</span> <span class=\"c1\">// Image that it's a gift</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Thread</span> <span class=\"kr\">thread</span><span class=\"p\">;</span> <span class=\"c1\">// And… thread is just like your girl</span>\n <span class=\"kr\">thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">runnable</span><span class=\"p\">);</span> <span class=\"c1\">// Okay, give your sweet babe the gift :)</span>\n <span class=\"kr\">thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2封装一个可运行线程的类\">2、封装一个可运行线程的类</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中实现了一个 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 类,该类继承了 <code class=\"language-plaintext highlighter-rouge\">Runnable</code>,就是上面那个 <code class=\"language-plaintext highlighter-rouge\">gift</code> 喽。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">StartableProcess</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Runnable</span><span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">StartableProcess</span><span class=\"p\">(</span><span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">startable</span><span class=\"p\">);</span>\n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">run</span><span class=\"p\">();</span>\n <span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">_startable</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>可以看到其中有 <code class=\"language-plaintext highlighter-rouge\">Startable& _startable</code> 引用成员,它并没有继承 <code class=\"language-plaintext highlighter-rouge\">Runnable</code>,而是封装了 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 和 <code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Thread</span> <span class=\"kr\">_thread</span><span class=\"p\">;</span>\n<span class=\"n\">StartableProcess</span> <span class=\"n\">_process</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>这里 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 封装了一个 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 成员,与 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\n\n<h4 id=\"3启动-rtmfpserver-线程\">3、启动 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</h4>\n<p>我们可以看到在 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的构造函数中初始化了 <code class=\"language-plaintext highlighter-rouge\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\"language-plaintext highlighter-rouge\">(Flag Field)_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">true</code>,因为它还没有调用启动函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_name</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">),</span>\n <span class=\"kr\">_thread</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">),</span>\n <span class=\"n\">_stop</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_haveToJoin</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">_process</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>初始化 <code class=\"language-plaintext highlighter-rouge\">_process</code> 时,调用 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 构造函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">StartableProcess</span><span class=\"o\">::</span><span class=\"n\">StartableProcess</span><span class=\"p\">(</span><span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">startable</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_startable</span><span class=\"p\">(</span><span class=\"n\">startable</span><span class=\"p\">){</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>传入 <code class=\"language-plaintext highlighter-rouge\">_startable</code> 的引用。在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的,然后通过调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 来启动,启动后会响应到 <code class=\"language-plaintext highlighter-rouge\">run()</code>。下面我们以 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程为例。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 类是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">RTMFPServer</span>\n <span class=\"o\">:</span> <span class=\"k\">private</span> <span class=\"n\">Gateway</span><span class=\"p\">,</span>\n <span class=\"k\">protected</span> <span class=\"n\">Handler</span><span class=\"p\">,</span>\n <span class=\"k\">private</span> <span class=\"n\">Startable</span><span class=\"p\">,</span>\n <span class=\"k\">private</span> <span class=\"n\">SocketHandler</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的构造函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">RTMFPServer</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">cores</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer\"</span><span class=\"p\">),</span>\n <span class=\"n\">_sendingEngine</span><span class=\"p\">(</span><span class=\"n\">cores</span><span class=\"p\">),</span>\n <span class=\"n\">_receivingEngine</span><span class=\"p\">(</span><span class=\"n\">cores</span><span class=\"p\">),</span>\n <span class=\"n\">_pCirrus</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_handshake</span><span class=\"p\">(</span><span class=\"n\">_receivingEngine</span><span class=\"p\">,</span>\n <span class=\"n\">_sendingEngine</span><span class=\"p\">,</span>\n <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">),</span>\n <span class=\"n\">_sessions</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\n\n<table>\n <thead>\n <tr>\n <th>所在线程</th>\n <th>调用者</th>\n <th>函数</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>主线程</td>\n <td>main(…)</td>\n <td> </td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::start()</td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer对象</td>\n <td>Startable::start()</td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer从Startable继承来的Thread成员</td>\n <td>Thread::start(…)</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\n <td>StartableProcess::run()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::prerun()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>Startable::prerun()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::run()</td>\n </tr>\n </tbody>\n</table>\n\n<h4 id=\"4rtmfpserver-线程等待\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程等待</h4>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">terminate</span><span class=\"p\">)</span>\n <span class=\"n\">handle</span><span class=\"p\">(</span><span class=\"n\">terminate</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">handle(…)</code> 函数很简单,如下只进行了 <code class=\"language-plaintext highlighter-rouge\">sleep(...)</code> 和 <code class=\"language-plaintext highlighter-rouge\">giveHandle()</code> 两个操作。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">handle</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">){</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">sleep</span><span class=\"p\">()</span> <span class=\"o\">!=</span> <span class=\"n\">STOP</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">giveHandle</span><span class=\"p\">();</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span>\n <span class=\"n\">terminate</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">sleep(…)</code> 是 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 是从 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 继承而来的,声明如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">WakeUpType</span> <span class=\"nf\">sleep</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>定义如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">WakeUpType</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"n\">WakeUpType</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">WAKEUP</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">tryWait</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">))</span>\n <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">TIMEOUT</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>在运行状态下,<code class=\"language-plaintext highlighter-rouge\">_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">false</code>,而默认参数 <code class=\"language-plaintext highlighter-rouge\">timeout</code> 为 0,所以会调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>这个 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent</code> 成员是一个 <code class=\"language-plaintext highlighter-rouge\">Poco::Event</code> 对象,<code class=\"language-plaintext highlighter-rouge\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\"language-plaintext highlighter-rouge\">Poco::Event::wait()</code> 后,会一直等待 <code class=\"language-plaintext highlighter-rouge\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\"language-plaintext highlighter-rouge\">wait</code> 的状态。在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中 <code class=\"language-plaintext highlighter-rouge\">set</code> 的动作是由:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPServer::requestHandle()</code></li>\n <li><code class=\"language-plaintext highlighter-rouge\">PoolThread::push(Poco::AutoPtr<RunnableType>& pRunnable)</code></li>\n</ul>\n\n<p>执行的。</p>\n\n<h3 id=\"二rtmfpmanager-对-rtmfpserver-的影响\">二、<code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的影响</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 与 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 同样,继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">RTMFPManager</span> <span class=\"o\">:</span> <span class=\"k\">private</span> <span class=\"n\">Task</span><span class=\"p\">,</span> <span class=\"k\">private</span> <span class=\"n\">Startable</span>\n</code></pre></div></div>\n\n<p>在构造函数中将 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\"language-plaintext highlighter-rouge\">_server</code> 引用成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">RTMFPManager</span><span class=\"p\">(</span><span class=\"n\">RTMFPServer</span><span class=\"o\">&</span> <span class=\"n\">server</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_server</span><span class=\"p\">(</span><span class=\"n\">server</span><span class=\"p\">),</span>\n <span class=\"n\">Task</span><span class=\"p\">(</span><span class=\"n\">server</span><span class=\"p\">),</span>\n <span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPManager\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">start</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n\n<span class=\"cm\">/* ...... */</span>\n\n<span class=\"n\">RTMFPServer</span><span class=\"o\">&</span> <span class=\"n\">_server</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的构造函数中调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 成员函数,是从 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的线程。然后响应到 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager::run()</code> 函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">setPriority</span><span class=\"p\">(</span><span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">PRIO_LOW</span><span class=\"p\">);</span>\n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">2000</span><span class=\"p\">)</span><span class=\"o\">!=</span><span class=\"n\">STOP</span><span class=\"p\">)</span>\n <span class=\"n\">waitHandle</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这里要强调的是,这里的 <code class=\"language-plaintext highlighter-rouge\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\n\n<p>在这里我们可以看到 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的 <code class=\"language-plaintext highlighter-rouge\">handle(…)</code> 中的 <code class=\"language-plaintext highlighter-rouge\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的主循环的等待时间的。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">WakeUpType</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"n\">WakeUpType</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">WAKEUP</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">tryWait</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">))</span>\n <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">TIMEOUT</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>你可以自行修改 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 中 <code class=\"language-plaintext highlighter-rouge\">sleep(...)</code> 的参数,这样就会调用 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\"language-plaintext highlighter-rouge\">timeout</code>)来进行睡眠。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\"language-plaintext highlighter-rouge\">handle</code> 成员函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">handle</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_server</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这里就会调用到 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 源码时知道 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::manage()</code> 函数并不是在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程内运行的,而是 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">manage</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\"language-plaintext highlighter-rouge\">Session</code>。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</title>\n \t<meta name=\"description\" content=\"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</h2>\t\t\n\t<time datetime=\"2012-07-23T03:07:43+00:00\" class=\"by-line\">23 Jul 2012, 广州 | 作者 麦克船长 | 总计 3111 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#1客户端发布publishing-on-client-side\" id=\"markdown-toc-1客户端发布publishing-on-client-side\">1、客户端发布(Publishing on client side)</a></li>\n <li><a href=\"#2服务器端server-side\" id=\"markdown-toc-2服务器端server-side\">2、服务器端(Server-side)</a></li>\n <li><a href=\"#3客户端订阅subscribing-on-client-side\" id=\"markdown-toc-3客户端订阅subscribing-on-client-side\">3、客户端订阅(Subscribing on client side)</a></li>\n <li><a href=\"#4reference\" id=\"markdown-toc-4reference\">4、Reference</a></li>\n</ul>\n\n<p>整个流程概括如下:</p>\n\n<p>Flash 客户端通过 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 与 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 建立连接,然后通过 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\n\n<h3 id=\"1客户端发布publishing-on-client-side\">1、客户端发布(Publishing on client side)</h3>\n\n<p>通过 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\"/2012/04/10/openrtmfp-cumulus-1/\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\"language-plaintext highlighter-rouge\">nc</code> 是一个 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 对象。</p>\n\n<div class=\"language-actionscript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">nc</span><span class=\"p\">.</span><span class=\"nx\">connect</span><span class=\"p\">(</span><span class=\"s2\">\"rtmfp://localhost:1935\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\"language-plaintext highlighter-rouge\">ns1</code> 是一个 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 对象。</p>\n\n<div class=\"language-actionscript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">ns1</span><span class=\"p\">.</span><span class=\"nx\">publish</span><span class=\"p\">(</span><span class=\"s2\">\"poechant_media_flow\"</span><span class=\"p\">,</span> <span class=\"s2\">\"live\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\n\n<h3 id=\"2服务器端server-side\">2、服务器端(Server-side)</h3>\n\n<p>Cumulus 通过 <code class=\"language-plaintext highlighter-rouge\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">receive</span><span class=\"p\">(</span><span class=\"n\">RTMFPReceiving</span><span class=\"o\">&</span> <span class=\"n\">rtmfpReceiving</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\n\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\"language-plaintext highlighter-rouge\">Handshake</code> 类的 <code class=\"language-plaintext highlighter-rouge\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">ServerSession</span><span class=\"o\">::</span><span class=\"n\">packetHandler</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">packet</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Flow</span><span class=\"o\">::</span><span class=\"n\">fragmentSortedHandler</span><span class=\"p\">(</span><span class=\"n\">UInt64</span> <span class=\"n\">stage</span><span class=\"p\">,</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">fragment</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">flags</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">switch</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AMF_WITH_HANDLER</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AMF</span><span class=\"p\">:</span>\n <span class=\"n\">messageHandler</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">amf</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AUDIO</span><span class=\"p\">:</span>\n <span class=\"n\">audioHandler</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">VIDEO</span><span class=\"p\">:</span>\n <span class=\"n\">videoHandler</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"nl\">default:</span>\n <span class=\"n\">rawHandler</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>接下来在 <code class=\"language-plaintext highlighter-rouge\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span> <span class=\"o\">!=</span> <span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushAudioPacket</span><span class=\"p\">(</span><span class=\"n\">time</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n \n<span class=\"k\">for</span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span><span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span><span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushVideoPacket</span><span class=\"p\">(</span><span class=\"n\">time</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n \n<span class=\"k\">for</span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span><span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span><span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushDataPacket</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中的 <code class=\"language-plaintext highlighter-rouge\">_listeners</code> 就是该 <code class=\"language-plaintext highlighter-rouge\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Listener</span><span class=\"o\">&</span> <span class=\"n\">addListener</span><span class=\"p\">(</span>\n <span class=\"n\">Peer</span><span class=\"o\">&</span> <span class=\"n\">peer</span><span class=\"p\">,</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">id</span><span class=\"p\">,</span>\n <span class=\"n\">FlowWriter</span><span class=\"o\">&</span> <span class=\"n\">writer</span><span class=\"p\">,</span>\n <span class=\"kt\">bool</span> <span class=\"n\">unbuffered</span><span class=\"p\">);</span>\n \n<span class=\"kt\">void</span> <span class=\"nf\">removeListener</span><span class=\"p\">(</span>\n <span class=\"n\">Peer</span><span class=\"o\">&</span> <span class=\"n\">peer</span><span class=\"p\">,</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">id</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这两个函数来实现的。</p>\n\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\n\n<h3 id=\"3客户端订阅subscribing-on-client-side\">3、客户端订阅(Subscribing on client side)</h3>\n\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>ns2.play(\"poechant_media_flow\");\n</code></pre></div></div>\n\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\"language-plaintext highlighter-rouge\">NetStream::send(…)</code> 的,用于发送 <code class=\"language-plaintext highlighter-rouge\">Data</code>,<code class=\"language-plaintext highlighter-rouge\">Audio</code> 和 <code class=\"language-plaintext highlighter-rouge\">Video</code> 的程序可以参考该例修改。</p>\n\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 初始化的时候,传入 <code class=\"language-plaintext highlighter-rouge\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\"language-plaintext highlighter-rouge\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\n\n<h3 id=\"4reference\">4、Reference</h3>\n\n<ul>\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\n</ul>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</h2>\t\t\n\t<time datetime=\"2012-06-25T02:56:26+00:00\" class=\"by-line\">25 Jun 2012, 广州 | 作者 麦克船长 | 总计 2111 字</time>\n\t<div class=\"content\">\n\t\t<p><code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中的线程都是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code>,在其中封装 <code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\"language-plaintext highlighter-rouge\">Startable</code> 中的 <code class=\"language-plaintext highlighter-rouge\">start</code> 函数如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">)</span> <span class=\"c1\">// if running</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n \n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span>\n <span class=\"s\">\"Try to start up a new thread inherited from Startable\"</span><span class=\"p\">);</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">_process</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> \n <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span>\n <span class=\"s\">\"Impossible to start the thread : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样一个类继承 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code>,然后调用到该类自己的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\"language-plaintext highlighter-rouge\">SocketManager</code> 为例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">SocketManager</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"err\">…</span> \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"err\">…</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>我们要看看这个 <code class=\"language-plaintext highlighter-rouge\">running()</code> 是怎么回事,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">bool</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">running</span><span class=\"p\">()</span> <span class=\"k\">const</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>很简单,就是通过 <code class=\"language-plaintext highlighter-rouge\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 是什么时候被设置为 <code class=\"language-plaintext highlighter-rouge\">false</code> 的呢?就是上面的 <code class=\"language-plaintext highlighter-rouge\">start()</code>,这里存在的一个问题就是先 <code class=\"language-plaintext highlighter-rouge\">start</code> 线程,再设置 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_thread.start(_process);\n_stop=false;\n</code></pre></div></div>\n\n<p>而 <code class=\"language-plaintext highlighter-rouge\">start()</code> 之后 <code class=\"language-plaintext highlighter-rouge\">run()</code> 的时候就开始通过 <code class=\"language-plaintext highlighter-rouge\">running()</code> 来判断 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 值了。所以你会在使用 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\" alt=\"image\" /></p>\n\n<p>它们是:</p>\n\n<ul>\n <li>主线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 线程</li>\n</ul>\n\n<p>而异常情况可能是 <code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 没有启动,甚至 <code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 都没有启动。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\" alt=\"image\" /></p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 都没有启动的情况 T.T</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\" alt=\"image\" /></p>\n\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\n\n<p>解决办法就是将 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 值为 <code class=\"language-plaintext highlighter-rouge\">true</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">)</span> <span class=\"c1\">// if running</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n \n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span>\n <span class=\"s\">\"Try to start up a new thread inherited from Startable\"</span><span class=\"p\">);</span>\n <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">_process</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> \n <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span> \n <span class=\"c1\">// June 25th, 2012, Michael@YY</span>\n <span class=\"p\">}</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span>\n <span class=\"s\">\"Impossible to start the thread : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</h2>\t\t\n\t<time datetime=\"2012-06-07T15:34:18+00:00\" class=\"by-line\">07 Jun 2012, 广州 | 作者 麦克船长 | 总计 1538 字</time>\n\t<div class=\"content\">\n\t\t<p>OpenRTMFP/Cumulus 提供了 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>。</p>\n\n<p>一般来说,Thread A 会准备好要 <code class=\"language-plaintext highlighter-rouge\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\"language-plaintext highlighter-rouge\">push</code> 消息。</p>\n\n<p>但是 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\"language-plaintext highlighter-rouge\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\" alt=\"image\" /></p>\n\n<p>由于在 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\"language-plaintext highlighter-rouge\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\"language-plaintext highlighter-rouge\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\" alt=\"image\" /></p>\n\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">createAMFMessage</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">)</span>\n \n <span class=\"c1\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"p\">(</span><span class=\"n\">_closed</span> <span class=\"o\">||</span> <span class=\"n\">signature</span><span class=\"p\">.</span><span class=\"n\">empty</span><span class=\"p\">()</span> <span class=\"o\">||</span> <span class=\"n\">_band</span><span class=\"p\">.</span><span class=\"n\">failed</span><span class=\"p\">()))</span> <span class=\"p\">{</span>\n <span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">MessageBuffered</span><span class=\"p\">();</span>\n <span class=\"n\">MessageBuffered</span><span class=\"o\">&</span> <span class=\"n\">message</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"n\">writeResponseHeader</span><span class=\"p\">(</span><span class=\"n\">message</span><span class=\"p\">.</span><span class=\"n\">rawWriter</span><span class=\"p\">,</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">pMessage</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">MessageBuffered</span><span class=\"o\">&</span> <span class=\"n\">message</span><span class=\"p\">(</span><span class=\"n\">_MessageNull</span><span class=\"p\">);</span>\n <span class=\"n\">writeResponseHeader</span><span class=\"p\">(</span><span class=\"n\">message</span><span class=\"p\">.</span><span class=\"n\">rawWriter</span><span class=\"p\">,</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">NULL</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>然后再调用时最后再增加 <code class=\"language-plaintext highlighter-rouge\">push</code> 操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"kt\">void</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">pushAMFMessage</span><span class=\"p\">(</span><span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMessage</span> <span class=\"o\">!=</span> <span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_messages</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\" alt=\"image\" /></p>\n\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"kt\">void</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">pushAMFMessage</span><span class=\"p\">(</span><span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMessage</span> <span class=\"o\">!=</span> <span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Mutex</span><span class=\"o\">::</span><span class=\"n\">ScopedLock</span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">msgQueueMutex</span><span class=\"p\">);</span>\n <span class=\"n\">_messages</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样就基本解决了这个线程安全问题。</p>\n\n<p>另外,使用 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>一名出色软件工程师的技术基本功:编程与工具</title>\n \t<meta name=\"description\" content=\"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>一名出色软件工程师的技术基本功:编程与工具</h2>\t\t\n\t<time datetime=\"2012-05-15T17:06:59+00:00\" class=\"by-line\">15 May 2012, 广州 | 作者 麦克船长 | 总计 2132 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"0写在前面\">0、写在前面</h3>\n\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\n\n<h3 id=\"1编程\">1、编程</h3>\n\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\n\n<h4 id=\"如果你精通c语言\">如果你精通 C 语言</h4>\n\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\n\n<h4 id=\"如果你精通c语言-1\">如果你精通 C++ 语言</h4>\n\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\n\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\n\n<h4 id=\"精通的关键还是针对语言核心来说的\">精通的关键,还是针对语言核心来说的。</h4>\n\n<p>第一,你要对这个语言的语法特性熟稔;</p>\n\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\n\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\n\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\n\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\n\n<h4 id=\"与计算机网络的基础结构相关联的技术实现\">与计算机、网络的基础结构相关联的技术实现</h4>\n\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\n\n<h3 id=\"2工具\">2、工具</h3>\n\n<p>对于工具有三个层面:</p>\n\n<p>第一,是熟练的使用一些工具。</p>\n\n<p>第二,是能够发现提高生产力的工具。</p>\n\n<p>第三,是能够在无可用工具时自己编写工具。</p>\n\n<p>那么都有哪些最最最基本的工具呢?</p>\n\n<h4 id=\"ideintegrateddevelopmentenvironment\">IDE(Integrated Development Environment)</h4>\n\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\n\n<h4 id=\"vcsversioncontrolsystem\">VCS(Version Control System)</h4>\n\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\n\n<blockquote>\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black & white.</p>\n</blockquote>\n\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\n\n<h4 id=\"clicommandlineinterface\">CLI(Command Line Interface)</h4>\n\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\n\n<h3 id=\"3结语\">3、结语</h3>\n\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</h2>\t\t\n\t<time datetime=\"2012-04-24T03:31:10+00:00\" class=\"by-line\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 12668 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一流缓冲区\" id=\"markdown-toc-一流缓冲区\">一、流缓冲区</a> <ul>\n <li><a href=\"#1了解-stdstreambuf\" id=\"markdown-toc-1了解-stdstreambuf\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::streambuf</code></a> <ul>\n <li><a href=\"#11单步移动内置指针\" id=\"markdown-toc-11单步移动内置指针\">1.1、单步移动内置指针</a></li>\n <li><a href=\"#12获取-get-指针和-put-指针的位置\" id=\"markdown-toc-12获取-get-指针和-put-指针的位置\">1.2、获取 get 指针和 put 指针的位置</a></li>\n <li><a href=\"#13设置-get-和-put-指针可达区域的上下界\" id=\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\">1.3、设置 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针可达区域的上下界</a></li>\n </ul>\n </li>\n <li><a href=\"#2memorystreambuf\" id=\"markdown-toc-2memorystreambuf\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code></a> <ul>\n <li><a href=\"#21移动内置的-get-和-put-指针\" id=\"markdown-toc-21移动内置的-get-和-put-指针\">2.1、移动内置的 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针:</a></li>\n <li><a href=\"#22获取-get-和-put-指针当前位置\" id=\"markdown-toc-22获取-get-和-put-指针当前位置\">2.2、获取 get 和 put 指针当前位置:</a></li>\n <li><a href=\"#23获取缓冲区的起始位置和大小\" id=\"markdown-toc-23获取缓冲区的起始位置和大小\">2.3、获取缓冲区的起始位置和大小:</a></li>\n <li><a href=\"#24缓冲区的已写字节数\" id=\"markdown-toc-24缓冲区的已写字节数\">2.4、缓冲区的已写字节数</a></li>\n <li><a href=\"#25显式设定-put-和-get-指针位置\" id=\"markdown-toc-25显式设定-put-和-get-指针位置\">2.5、显式设定 <code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针位置</a></li>\n <li><a href=\"#26-修改缓冲区大小\" id=\"markdown-toc-26-修改缓冲区大小\">2.6 修改缓冲区大小</a></li>\n <li><a href=\"#27构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-27构造函数拷贝构造函数和析构函数\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#二io-流\" id=\"markdown-toc-二io-流\">二、IO 流</a> <ul>\n <li><a href=\"#1了解-stdios\" id=\"markdown-toc-1了解-stdios\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::ios</code></a></li>\n <li><a href=\"#2memoryios\" id=\"markdown-toc-2memoryios\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code></a> <ul>\n <li><a href=\"#21构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-21构造函数拷贝构造函数和析构函数\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\n <li><a href=\"#22得到-memorystreambuf-成员的地址\" id=\"markdown-toc-22得到-memorystreambuf-成员的地址\">2.2、得到 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的地址</a></li>\n <li><a href=\"#23当前位置\" id=\"markdown-toc-23当前位置\">2.3、当前位置</a></li>\n <li><a href=\"#24封装-memorystreambuf-成员的一些函数\" id=\"markdown-toc-24封装-memorystreambuf-成员的一些函数\">2.4、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的一些函数</a></li>\n <li><a href=\"#25-缓冲区可读数据的字节数\" id=\"markdown-toc-25-缓冲区可读数据的字节数\">2.5 缓冲区可读数据的字节数</a></li>\n </ul>\n </li>\n <li><a href=\"#3输入流\" id=\"markdown-toc-3输入流\">3、输入流</a></li>\n <li><a href=\"#4输出流\" id=\"markdown-toc-4输出流\">4、输出流</a> <ul>\n <li><a href=\"#41-构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-41-构造函数拷贝构造函数和析构函数\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\n <li><a href=\"#42-读取和设定已写字节数\" id=\"markdown-toc-42-读取和设定已写字节数\">4.2 读取和设定已写字节数</a></li>\n <li><a href=\"#43-当前位置\" id=\"markdown-toc-43-当前位置\">4.3 当前位置</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#三局部内存片\" id=\"markdown-toc-三局部内存片\">三、局部内存片</a> <ul>\n <li><a href=\"#1构造函数\" id=\"markdown-toc-1构造函数\">1、构造函数</a></li>\n <li><a href=\"#2析构函数\" id=\"markdown-toc-2析构函数\">2、析构函数</a></li>\n <li><a href=\"#3缓冲区切割\" id=\"markdown-toc-3缓冲区切割\">3、缓冲区切割</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\n\n<h3 id=\"一流缓冲区\">一、流缓冲区</h3>\n\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\n\n<h4 id=\"1了解-stdstreambuf\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::streambuf</code></h4>\n\n<p>首先要了解 <code class=\"language-plaintext highlighter-rouge\">streambuf</code> 内置了一个 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针和一个 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针。<code class=\"language-plaintext highlighter-rouge\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\"language-plaintext highlighter-rouge\">g</code> 和 <code class=\"language-plaintext highlighter-rouge\">p</code> 就分别表示 get pointer 和 put pointer。</p>\n\n<h5 id=\"11单步移动内置指针\">1.1、单步移动内置指针</h5>\n\n<p>Increase get pointer: Advances the get pointer by <code class=\"language-plaintext highlighter-rouge\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">gbump</span> <span class=\"p\">(</span> <span class=\"kt\">int</span> <span class=\"n\">n</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>Increase put pointer: Advances the put pointer by <code class=\"language-plaintext highlighter-rouge\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">pbump</span> <span class=\"p\">(</span> <span class=\"kt\">int</span> <span class=\"n\">n</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<h5 id=\"12获取-get-指针和-put-指针的位置\">1.2、获取 get 指针和 put 指针的位置</h5>\n\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">char</span> <span class=\"o\">*</span> <span class=\"n\">gptr</span> <span class=\"p\">(</span> <span class=\"p\">)</span> <span class=\"k\">const</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">char</span> <span class=\"o\">*</span> <span class=\"n\">pptr</span> <span class=\"p\">(</span> <span class=\"p\">)</span> <span class=\"k\">const</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h5 id=\"13设置-get-和-put-指针可达区域的上下界\">1.3、设置 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针可达区域的上下界</h5>\n\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">setg</span> <span class=\"p\">(</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gbeg</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gnext</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gend</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\n</ul>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">setp</span> <span class=\"p\">(</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pbeg</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pend</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\n</ul>\n\n<h4 id=\"2memorystreambuf\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code></h4>\n\n<p>类定义:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryStreamBuf</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">streambuf</span> <span class=\"p\">{</span>\n <span class=\"k\">friend</span> <span class=\"k\">class</span> <span class=\"nc\">ScopedMemoryClip</span><span class=\"p\">;</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">written</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gCurrent</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pCurrent</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n \n<span class=\"nl\">private:</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">overflow</span><span class=\"p\">(</span><span class=\"n\">int_type</span> <span class=\"n\">c</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">underflow</span><span class=\"p\">();</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">sync</span><span class=\"p\">();</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_written</span><span class=\"p\">;</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">();</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"k\">operator</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span><span class=\"p\">);</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 是 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\n\n<h5 id=\"21移动内置的-get-和-put-指针\">2.1、移动内置的 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针:</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针都移动:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">gbump</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22获取-get-和-put-指针当前位置\">2.2、获取 get 和 put 指针当前位置:</h5>\n\n<p>封装 <code class=\"language-plaintext highlighter-rouge\">streambuf</code> 的 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 和 <code class=\"language-plaintext highlighter-rouge\">pptr</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">gCurrent</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">gptr</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">pptr</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23获取缓冲区的起始位置和大小\">2.3、获取缓冲区的起始位置和大小:</h5>\n\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"24缓冲区的已写字节数\">2.4、缓冲区的已写字节数</h5>\n\n<p>读取(其中也可能发生设置操作):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"kt\">int</span> <span class=\"n\">written</span> <span class=\"o\">=</span> <span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"c1\">// 已写字节数</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">written</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">written</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">written</span> <span class=\"o\">></span> <span class=\"n\">_written</span><span class=\"p\">)</span> <span class=\"c1\">// 保存已写字节数</span>\n <span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"p\">)</span><span class=\"n\">written</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">_written</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设置:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_written</span><span class=\"o\">=</span><span class=\"n\">size</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"25显式设定-put-和-get-指针位置\">2.5、显式设定 <code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针位置</h5>\n\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"c1\">// 保存已写字节数</span>\n <span class=\"n\">written</span><span class=\"p\">();</span> <span class=\"c1\">// Save nb char written</span>\n \n <span class=\"c1\">// 移动 put 指针</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n <span class=\"n\">pbump</span><span class=\"p\">((</span><span class=\"kt\">int</span><span class=\"p\">)</span> <span class=\"n\">pos</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 移动 get 指针</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">pos</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"26-修改缓冲区大小\">2.6 修改缓冲区大小</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"c1\">// 大小标识</span>\n <span class=\"n\">_bufferSize</span> <span class=\"o\">=</span> <span class=\"n\">newSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// gptr 当前位置</span>\n <span class=\"kt\">int</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">gCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 gptr 可达范围和当前位置</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">pos</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span> \n <span class=\"c1\">// pptr 当前位置</span>\n <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 pptr 可达范围和当前位置</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"27构造函数拷贝构造函数和析构函数\">2.7、构造函数、拷贝构造函数和析构函数</h5>\n\n<p>构造函数会设定 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 和 <code class=\"language-plaintext highlighter-rouge\">gptr</code>,并初始化 <code class=\"language-plaintext highlighter-rouge\">pBuffer</code> 和 <code class=\"language-plaintext highlighter-rouge\">bufferSize</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> <span class=\"n\">_pBuffer</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">),</span><span class=\"n\">_bufferSize</span><span class=\"p\">(</span><span class=\"n\">bufferSize</span><span class=\"p\">),</span><span class=\"n\">_written</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span><span class=\"p\">,</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构函数会拷贝对方的 <code class=\"language-plaintext highlighter-rouge\">pBuffer</code>、<code class=\"language-plaintext highlighter-rouge\">bufferSizse</code>、<code class=\"language-plaintext highlighter-rouge\">_written</code>,并设定 <code class=\"language-plaintext highlighter-rouge\">gptr</code>、<code class=\"language-plaintext highlighter-rouge\">pptr</code>。注意设定 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 时,要分别调用 <code class=\"language-plaintext highlighter-rouge\">setp</code> 和 <code class=\"language-plaintext highlighter-rouge\">pbump</code>,因为 <code class=\"language-plaintext highlighter-rouge\">setp</code> 仅将 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span> <span class=\"n\">_pBuffer</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">),</span><span class=\"n\">_bufferSize</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">),</span><span class=\"n\">_written</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_written</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">gCurrent</span><span class=\"p\">(),</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">pbump</span><span class=\"p\">((</span><span class=\"kt\">int</span><span class=\"p\">)(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"n\">_pBuffer</span><span class=\"p\">));</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::~</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"二io-流\">二、IO 流</h3>\n\n<h4 id=\"1了解-stdios\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::ios</code></h4>\n\n<p>Initialize object [<code class=\"language-plaintext highlighter-rouge\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">init</span> <span class=\"p\">(</span> <span class=\"n\">streambuf</span><span class=\"o\">*</span> <span class=\"n\">sb</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>初始化后如下函数的返回值:</p>\n\n<table>\n <thead>\n <tr>\n <th>member function</th>\n <th>value</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>rdbuf()</td>\n <td>sb</td>\n </tr>\n <tr>\n <td>tie()</td>\n <td>0</td>\n </tr>\n <tr>\n <td>rdstate()</td>\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\n </tr>\n <tr>\n <td>exceptions()</td>\n <td>goodbit</td>\n </tr>\n <tr>\n <td>flags()</td>\n <td>skipws | dec</td>\n </tr>\n <tr>\n <td>width()</td>\n <td>0</td>\n </tr>\n <tr>\n <td>precision()</td>\n <td>6</td>\n </tr>\n <tr>\n <td>fill()</td>\n <td>‘ ’ (whitespace)</td>\n </tr>\n <tr>\n <td>getloc()</td>\n <td>a copy of locale()</td>\n </tr>\n </tbody>\n</table>\n\n<h4 id=\"2memoryios\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code></h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code>,且是 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 和 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\"language-plaintext highlighter-rouge\">std::ios</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryIOS</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"k\">virtual</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ios</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">MemoryIOS</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryIOS</span><span class=\"p\">();</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">*</span> <span class=\"n\">rdbuf</span><span class=\"p\">();</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">);</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"n\">MemoryStreamBuf</span> <span class=\"n\">_buf</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"21构造函数拷贝构造函数和析构函数\">2.1、构造函数、拷贝构造函数和析构函数</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span><span class=\"n\">_buf</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">poco_ios_init</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">poco_ios_init</code> 为 <code class=\"language-plaintext highlighter-rouge\">init</code> 的宏定义,用于初始化成员 <code class=\"language-plaintext highlighter-rouge\">_buf</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">MemoryIOS</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span><span class=\"n\">_buf</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_buf</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">poco_ios_init</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::~</span><span class=\"n\">MemoryIOS</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22得到-memorystreambuf-成员的地址\">2.2、得到 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的地址</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">*</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">rdbuf</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23当前位置\">2.3、当前位置</h5>\n\n<p>这是一个纯虚函数,由 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 和 <code class=\"language-plaintext highlighter-rouge\">MemoryOutpuStream</code> 继承时实现:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">virtual</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h5 id=\"24封装-memorystreambuf-成员的一些函数\">2.4、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的一些函数</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">resize</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">newSize</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code> 封装为 <code class=\"language-plaintext highlighter-rouge\">reset</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"o\">>=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"25-缓冲区可读数据的字节数\">2.5 缓冲区可读数据的字节数</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"kt\">int</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"p\">(</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">begin</span><span class=\"p\">());</span> <span class=\"c1\">// 缓冲区剩余可读数据字节数</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">result</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"p\">)</span><span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3输入流\">3、输入流</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryInputStream</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">MemoryIOS</span><span class=\"p\">,</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">istream</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"c1\">/// Creates a MemoryInputStream for the given memory area,</span>\n <span class=\"c1\">/// ready for reading.</span>\n <span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryInputStream</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">();</span>\n <span class=\"c1\">/// Destroys the MemoryInputStream.</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 以及 <code class=\"language-plaintext highlighter-rouge\">istream</code>。<code class=\"language-plaintext highlighter-rouge\">istream</code> 是 <code class=\"language-plaintext highlighter-rouge\">iostream</code> 中的 <code class=\"language-plaintext highlighter-rouge\">basic_istream</code> 别名(<code class=\"language-plaintext highlighter-rouge\">typedef</code>)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> \n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"k\">const_cast</span><span class=\"o\"><</span><span class=\"kt\">char</span><span class=\"o\">*></span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">),</span> <span class=\"n\">bufferSize</span><span class=\"p\">),</span> <span class=\"n\">istream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryInputStream</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">),</span> <span class=\"n\">istream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryInputStream</span><span class=\"o\">::~</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>唯一的一个成员函数是 <code class=\"language-plaintext highlighter-rouge\">current</code>,封装了 <code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 的 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的 <code class=\"language-plaintext highlighter-rouge\">gCurrent</code> 函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">gCurrent</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4输出流\">4、输出流</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryOutputStream</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">MemoryIOS</span><span class=\"p\">,</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ostream</span>\n <span class=\"c1\">/// An input stream for reading from a memory area.</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"c1\">/// Creates a MemoryOutputStream for the given memory area,</span>\n <span class=\"c1\">/// ready for writing.</span>\n <span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryOutputStream</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">();</span>\n <span class=\"c1\">/// Destroys the MemoryInputStream.</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">written</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"41-构造函数拷贝构造函数和析构函数\">4.1 构造函数、拷贝构造函数和析构函数</h5>\n\n<p>如下,不赘述了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> \n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">bufferSize</span><span class=\"p\">),</span> <span class=\"n\">ostream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n<span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryOutputStream</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">),</span> <span class=\"n\">ostream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryOutputStream</span><span class=\"o\">::~</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(){</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"42-读取和设定已写字节数\">4.2 读取和设定已写字节数</h5>\n\n<p>读取:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">written</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"43-当前位置\">4.3 当前位置</h5>\n\n<p>与 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 中的封装类似:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">pCurrent</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三局部内存片\">三、局部内存片</h3>\n\n<p>在第一部分的流缓冲区介绍 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 中有一个 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ScopedMemoryClip</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">ScopedMemoryClip</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">offset</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Int32</span> <span class=\"n\">offset</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_offset</span><span class=\"p\">;</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">_buffer</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"1构造函数\">1、构造函数</h4>\n\n<p>构造函数传入的参数对应的就是 <code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\"language-plaintext highlighter-rouge\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">offset</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_offset</span><span class=\"p\">(</span><span class=\"n\">offset</span><span class=\"p\">),</span> <span class=\"n\">_buffer</span><span class=\"p\">(</span><span class=\"n\">buffer</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_offset</span> <span class=\"o\">>=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">_offset</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span> <span class=\"o\">-</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_offset</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_offset</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">_offset</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2析构函数\">2、析构函数</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::~</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"o\">-</span><span class=\"p\">(</span><span class=\"n\">Int32</span><span class=\"p\">)</span><span class=\"n\">_offset</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3缓冲区切割\">3、缓冲区切割</h4>\n\n<p>可以看到构造函数和析构函数中都调用了 <code class=\"language-plaintext highlighter-rouge\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\n\n<ul>\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\n</ul>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::</span><span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">Int32</span> <span class=\"n\">offset</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"c1\">// 获取到 gptr</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gpos</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">gCurrent</span><span class=\"p\">();</span>\n \n <span class=\"c1\">// 偏移缓冲区地址,并修改缓冲区大小</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span> <span class=\"o\">-=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\n <span class=\"kt\">int</span> <span class=\"n\">ppos</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 gptr 可达区域和位置</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">gpos</span><span class=\"p\">,</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 设置 pptr 可达区域和位置</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">ppos</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\"><</span> <span class=\"n\">offset</span><span class=\"p\">)</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\n <span class=\"k\">else</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">-=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">></span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</h2>\t\t\n\t<time datetime=\"2012-04-24T02:04:55+00:00\" class=\"by-line\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 30820 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一amf-数据类型定义\" id=\"markdown-toc-一amf-数据类型定义\">一、AMF 数据类型定义</a> <ul>\n <li><a href=\"#1数据类型\" id=\"markdown-toc-1数据类型\">1、数据类型</a></li>\n <li><a href=\"#2undefined-type\" id=\"markdown-toc-2undefined-type\">2、<code class=\"language-plaintext highlighter-rouge\">undefined</code> Type</a></li>\n <li><a href=\"#3null-type\" id=\"markdown-toc-3null-type\">3、<code class=\"language-plaintext highlighter-rouge\">null</code> Type</a></li>\n <li><a href=\"#4false-type\" id=\"markdown-toc-4false-type\">4、<code class=\"language-plaintext highlighter-rouge\">false</code> type</a></li>\n <li><a href=\"#5true-type\" id=\"markdown-toc-5true-type\">5、<code class=\"language-plaintext highlighter-rouge\">true</code> type</a></li>\n <li><a href=\"#6integer-type\" id=\"markdown-toc-6integer-type\">6、<code class=\"language-plaintext highlighter-rouge\">integer</code> type</a></li>\n <li><a href=\"#7double-type\" id=\"markdown-toc-7double-type\">7、<code class=\"language-plaintext highlighter-rouge\">double</code> type</a></li>\n <li><a href=\"#8string-type\" id=\"markdown-toc-8string-type\">8、<code class=\"language-plaintext highlighter-rouge\">String</code> type</a></li>\n <li><a href=\"#9xmldocument-type\" id=\"markdown-toc-9xmldocument-type\">9、<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> type</a></li>\n <li><a href=\"#10date-type\" id=\"markdown-toc-10date-type\">10、<code class=\"language-plaintext highlighter-rouge\">Date</code> type</a></li>\n <li><a href=\"#11array-type\" id=\"markdown-toc-11array-type\">11、<code class=\"language-plaintext highlighter-rouge\">Array</code> type</a></li>\n <li><a href=\"#12object-type\" id=\"markdown-toc-12object-type\">12、<code class=\"language-plaintext highlighter-rouge\">Object</code> type</a></li>\n <li><a href=\"#13xml-type\" id=\"markdown-toc-13xml-type\">13、<code class=\"language-plaintext highlighter-rouge\">XML</code> type</a></li>\n <li><a href=\"#14bytearray-type\" id=\"markdown-toc-14bytearray-type\">14、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> type</a></li>\n <li><a href=\"#15amf3-的使用\" id=\"markdown-toc-15amf3-的使用\">15、AMF3 的使用</a> <ul>\n <li><a href=\"#151netconnection-and-amf-3\" id=\"markdown-toc-151netconnection-and-amf-3\">15.1、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> and AMF 3</a></li>\n <li><a href=\"#152netconnection-in-actionscript-30\" id=\"markdown-toc-152netconnection-in-actionscript-30\">15.2、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> in ActionScript 3.0</a></li>\n <li><a href=\"#153bytearray-idatainput-and-idataoutput\" id=\"markdown-toc-153bytearray-idatainput-and-idataoutput\">15.3、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code>, <code class=\"language-plaintext highlighter-rouge\">IDataInput</code> and <code class=\"language-plaintext highlighter-rouge\">IDataOutput</code></a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#二binaryreaderwriter\" id=\"markdown-toc-二binaryreaderwriter\">二、<code class=\"language-plaintext highlighter-rouge\">BinaryReader/Writer</code></a> <ul>\n <li><a href=\"#1amf3-数据格式基础\" id=\"markdown-toc-1amf3-数据格式基础\">1、AMF3 数据格式基础</a></li>\n <li><a href=\"#2序列化\" id=\"markdown-toc-2序列化\">2、序列化</a></li>\n <li><a href=\"#3反序列化\" id=\"markdown-toc-3反序列化\">3、反序列化</a></li>\n </ul>\n </li>\n <li><a href=\"#三packetreaderwriter\" id=\"markdown-toc-三packetreaderwriter\">三、<code class=\"language-plaintext highlighter-rouge\">PacketReader/Writer</code></a> <ul>\n <li><a href=\"#1packetreader\" id=\"markdown-toc-1packetreader\">1、PacketReader</a> <ul>\n <li><a href=\"#11封装-memoryinputstream\" id=\"markdown-toc-11封装-memoryinputstream\">1.1、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code></a></li>\n <li><a href=\"#12收缩缓冲区\" id=\"markdown-toc-12收缩缓冲区\">1.2、收缩缓冲区</a></li>\n <li><a href=\"#13构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-13构造函数拷贝构造函数和析构函数\">1.3、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n <li><a href=\"#2packetwriter\" id=\"markdown-toc-2packetwriter\">2、<code class=\"language-plaintext highlighter-rouge\">PacketWriter</code></a> <ul>\n <li><a href=\"#21封装memoryoutputstream\" id=\"markdown-toc-21封装memoryoutputstream\">2.1、封装<code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code></a></li>\n <li><a href=\"#22封装-binarywriter\" id=\"markdown-toc-22封装-binarywriter\">2.2、封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code></a></li>\n <li><a href=\"#23构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-23构造函数拷贝构造函数和析构函数\">2.3、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四amfreader\" id=\"markdown-toc-四amfreader\">四、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code></a> <ul>\n <li><a href=\"#1objectdef\" id=\"markdown-toc-1objectdef\">1、<code class=\"language-plaintext highlighter-rouge\">ObjectDef</code></a></li>\n <li><a href=\"#2amfreader-定义\" id=\"markdown-toc-2amfreader-定义\">2、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 定义</a> <ul>\n <li><a href=\"#21构造函数析构函数\" id=\"markdown-toc-21构造函数析构函数\">2.1、构造函数、析构函数</a></li>\n <li><a href=\"#22简单封装-packetreader-的一些函数\" id=\"markdown-toc-22简单封装-packetreader-的一些函数\">2.2、简单封装 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 的一些函数</a></li>\n <li><a href=\"#23设置-gptr-位置\" id=\"markdown-toc-23设置-gptr-位置\">2.3、设置 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 位置</a></li>\n <li><a href=\"#24判断类型\" id=\"markdown-toc-24判断类型\">2.4、判断类型</a></li>\n </ul>\n </li>\n <li><a href=\"#3解析-as3-null\" id=\"markdown-toc-3解析-as3-null\">3、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Null</code></a></li>\n <li><a href=\"#4解析-as3-number\" id=\"markdown-toc-4解析-as3-number\">4、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Number</code></a></li>\n <li><a href=\"#5解析-as3-integer\" id=\"markdown-toc-5解析-as3-integer\">5、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Integer</code></a></li>\n <li><a href=\"#6解析-as3-boolean\" id=\"markdown-toc-6解析-as3-boolean\">6、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Boolean</code></a></li>\n <li><a href=\"#7开始引用与结束引用\" id=\"markdown-toc-7开始引用与结束引用\">7、开始引用与结束引用</a></li>\n <li><a href=\"#8解析-as3-bytearray\" id=\"markdown-toc-8解析-as3-bytearray\">8、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code></a></li>\n <li><a href=\"#9解析-as3-date\" id=\"markdown-toc-9解析-as3-date\">9、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Date</code></a></li>\n <li><a href=\"#10解析-as3-dictionary\" id=\"markdown-toc-10解析-as3-dictionary\">10、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Dictionary</code></a></li>\n </ul>\n </li>\n</ul>\n\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。</p>\n\n<h3 id=\"一amf-数据类型定义\">一、AMF 数据类型定义</h3>\n\n<h4 id=\"1数据类型\">1、数据类型</h4>\n\n<p>各种数据类型的标示都在 AMF.h 中定义为宏</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cp\">#define AMF_NUMBER 0x00 // 浮点数\n#define AMF_BOOLEAN 0x01 // 布尔型\n#define AMF_STRING 0x02 // 字符串\n#define AMF_BEGIN_OBJECT 0x03 // 对象,开始\n#define AMF_NULL 0x05 // null\n#define AMF_UNDEFINED 0x06\n#define AMF_REFERENCE 0x07\n#define AMF_MIXED_ARRAY 0x08\n#define AMF_END_OBJECT 0x09 // 对象,结束\n#define AMF_BEGIN_TYPED_OBJECT 0x10\n#define AMF_STRICT_ARRAY 0x0A\n#define AMF_DATE 0x0B // 日期\n#define AMF_LONG_STRING 0x0C // 字符串\n#define AMF_UNSUPPORTED 0x0D\n</span> \n<span class=\"cp\">#define AMF_AVMPLUS_OBJECT 0x11\n#define AMF_END 0xFF\n</span> \n<span class=\"cp\">#define AMF3_UNDEFINED 0x00\n#define AMF3_NULL 0x01\n#define AMF3_FALSE 0x02\n#define AMF3_TRUE 0x03\n#define AMF3_INTEGER 0x04\n#define AMF3_NUMBER 0x05\n#define AMF3_STRING 0x06\n#define AMF3_DATE 0x08\n#define AMF3_ARRAY 0x09\n#define AMF3_OBJECT 0x0A\n#define AMF3_BYTEARRAY 0x0C\n#define AMF3_DICTIONARY 0x11\n</span></code></pre></div></div>\n\n<p>并定义了一个枚举类表示数据类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">AMF</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"k\">enum</span> <span class=\"n\">Type</span> <span class=\"p\">{</span>\n <span class=\"n\">Null</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">,</span>\n <span class=\"n\">Boolean</span><span class=\"p\">,</span>\n <span class=\"n\">Integer</span><span class=\"p\">,</span>\n <span class=\"n\">Number</span><span class=\"p\">,</span>\n <span class=\"n\">String</span><span class=\"p\">,</span>\n <span class=\"n\">Date</span><span class=\"p\">,</span>\n <span class=\"n\">Array</span><span class=\"p\">,</span>\n <span class=\"n\">Object</span><span class=\"p\">,</span>\n <span class=\"n\">ByteArray</span><span class=\"p\">,</span>\n <span class=\"n\">Dictionary</span><span class=\"p\">,</span>\n <span class=\"n\">RawObjectContent</span><span class=\"p\">,</span>\n <span class=\"n\">End</span>\n <span class=\"p\">};</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"2undefined-type\">2、<code class=\"language-plaintext highlighter-rouge\">undefined</code> Type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">undefined</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">undefined</code> 类型标记表示。此值不会编码任何其他信息。</p>\n\n<h4 id=\"3null-type\">3、<code class=\"language-plaintext highlighter-rouge\">null</code> Type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">null</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">null</code> 类型标记表示。此值不会编码任何其他信息。</p>\n\n<h4 id=\"4false-type\">4、<code class=\"language-plaintext highlighter-rouge\">false</code> type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">false</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">false</code> 类型标记表示,用于编码布尔值 <code class=\"language-plaintext highlighter-rouge\">false</code>。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\n\n<h4 id=\"5true-type\">5、<code class=\"language-plaintext highlighter-rouge\">true</code> type</h4>\n\n<p>true 类型由 true 类型标记表示,用于编码布尔值 true。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\n\n<h4 id=\"6integer-type\">6、<code class=\"language-plaintext highlighter-rouge\">integer</code> type</h4>\n\n<p>在 AMF 3 中,整数使用可变长度的无符号 29 位整数进行序列化。ActionScript 3.0 中的整数类型 - 有符号 <code class=\"language-plaintext highlighter-rouge\">int</code> 类型和无符号 <code class=\"language-plaintext highlighter-rouge\">uint</code> 类型 - 也使用 29 位在 AVM+中表示。如果无符号整数 (<code class=\"language-plaintext highlighter-rouge\">uint</code>) 的值大于等于 229 或者如果有符号整数 (<code class=\"language-plaintext highlighter-rouge\">int</code>) 的值大于等于 228,则它将被 AVM+ 表示为 <code class=\"language-plaintext highlighter-rouge\">double</code> 类型,并使用 AMF 3 double 类型进行序列化。</p>\n\n<h4 id=\"7double-type\">7、<code class=\"language-plaintext highlighter-rouge\">double</code> type</h4>\n\n<p>AMF 3 的 <code class=\"language-plaintext highlighter-rouge\">double</code> 类型与 AMF 0 的 <code class=\"language-plaintext highlighter-rouge\">Number</code> 类型编码方式相同。此类型用于编码 ActionScript <code class=\"language-plaintext highlighter-rouge\">Number</code> 或值大于等于 228 的 ActionScript <code class=\"language-plaintext highlighter-rouge\">int</code> 或值大于等于 229 的 ActionScript <code class=\"language-plaintext highlighter-rouge\">uint</code>。编码值始终是网络字节顺序中的 8 字节 IEEE-754 双精度浮点值 (低内存中的符号位)。</p>\n\n<h4 id=\"8string-type\">8、<code class=\"language-plaintext highlighter-rouge\">String</code> type</h4>\n\n<p>ActionScript String 值使用 AMF 3 中的单个 string 类型表示 - AMF 0 中的 <code class=\"language-plaintext highlighter-rouge\">string</code> 和 <code class=\"language-plaintext highlighter-rouge\">long string</code> 类型的概念不再使用。可以使用对隐式字符串引用表中的索引将字符串作为先前发生的字符串的引用发送。字符串使用 UTF-8 编码 - 但是头可以描述字符串文本或字符串引用。空字符串永远不会作为引用发送。</p>\n\n<h4 id=\"9xmldocument-type\">9、<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> type</h4>\n\n<p>ActionScript 3.0 引入了新的 XML 类型 (参见 3.13),但是旧版的 XMLDocument 类型在语言中被保留为 <code class=\"language-plaintext highlighter-rouge\">flash.xml.XMLDocument</code>。与 AMF 0 类似,<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> 的结构需要扁平化为字符串表示以进行序列化。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。XMLDocuments 可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> 实例的引用发送。</p>\n\n<h4 id=\"10date-type\">10、<code class=\"language-plaintext highlighter-rouge\">Date</code> type</h4>\n\n<p>在 AMF 3 中,ActionScript Date 简单地作为自 1970 年 1 月 1 日午夜 (UTC 时区) 以来的毫秒数进行序列化。不发送本地时区信息。可以使用对隐式对象引用表中的索引将日期作为先前发生的日期实例的引用发送。</p>\n\n<h4 id=\"11array-type\">11、<code class=\"language-plaintext highlighter-rouge\">Array</code> type</h4>\n\n<p>ActionScript 数组的类型和在数组中的位置是基于它们的索引性质描述的。以下表格概述了这些术语的含义:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">strict</code>:仅包含序数(数字)索引</li>\n <li><code class=\"language-plaintext highlighter-rouge\">dense</code>:序数索引从 0 开始,并且在连续索引之间不存在间隙(即,从 0 到数组长度的每一个索引都被定义了)</li>\n <li><code class=\"language-plaintext highlighter-rouge\">sparse</code>:包含至少两个索引之间的一个间隙</li>\n <li><code class=\"language-plaintext highlighter-rouge\">associative</code>:包含至少一个非序数(字符串)索引(有时称为 ECMA 数组)</li>\n</ul>\n\n<p>AMF 将数组分为两部分,密集部分和关联部分。关联部分的二进制表示由名称/值对(可能没有)终止的空字符串。密集部分的二进制表示由密集部分的大小(可能为零)以及有序的值列表(可能没有)组成。在 AMF 中写入的顺序是密集部分的大小,一个以空字符串终止的名称/值对列表,然后是大小的值。数组可以通过使用隐式对象引用表的索引作为先前发生的数组的引用来发送。</p>\n\n<h4 id=\"12object-type\">12、<code class=\"language-plaintext highlighter-rouge\">Object</code> type</h4>\n\n<p>AMF 3 中有一种类型用于处理 ActionScript 对象和自定义用户类。使用术语 “traits” 来描述类的定义特征。除了 “anonymous” 对象和 “typed” 对象,ActionScript 3.0 还引入了两个进一步的 traits 来描述如何序列化对象,即 “dynamic” 和 “externalizable”。以下表格概述了这些术语和它们的含义:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">Anonymous</code>:实际的 ActionScript 对象类型的实例或没有注册别名的类的实例(在反序列化时将其视为对象)。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Typed</code>:具有注册别名的类的实例。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Dynamic</code>:具有动态特征声明的类定义的实例;可以在运行时动态地从实例中添加和删除公共变量成员。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Externalizable</code>:实现 flash.utils.IExternalizable 的类的实例,它完全控制其成员的序列化(特征信息中不包含属性名)。</li>\n</ul>\n\n<p>在这些特征之外,对象的特征信息还可能包括在类上定义的一组公共变量和公共可读写属性名称(即不是函数的公共成员)。成员名称的顺序很重要,因为在特征信息之后的成员值将按照完全相同的顺序出现。这些成员被视为密封成员,因为它们是由类型明确定义的。</p>\n\n<p>如果类型是动态的,则在密封成员之后可以包括一个进一步的部分,该部分将动态成员列为名称/值对。当遇到空字符串名称时,继续读取动态成员。</p>\n\n<p>对象可以通过使用隐式对象引用表中的索引来作为先前发生对象的引用。此外,还可以通过使用隐式特征引用表中的索引将特征信息发送为先前发生的一组特征的引用。</p>\n\n<h4 id=\"13xml-type\">13、<code class=\"language-plaintext highlighter-rouge\">XML</code> type</h4>\n\n<p>ActionScript 3.0 引入了一种新的 <code class=\"language-plaintext highlighter-rouge\">XML</code> 类型,支持 E4X 语法。为了序列化,需要将 <code class=\"language-plaintext highlighter-rouge\">XML</code> 类型展平成字符串表示形式。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。<code class=\"language-plaintext highlighter-rouge\">XML</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 XML 实例的引用发送。请注意,这种编码对 <code class=\"language-plaintext highlighter-rouge\">XML</code> 的使用造成了一些理论限制。每个 UTF-8 编码的 <code class=\"language-plaintext highlighter-rouge\">XML</code> 实例的字节长度最大为 228-1 字节(大约 256 MB)。</p>\n\n<h4 id=\"14bytearray-type\">14、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> type</h4>\n\n<p>用于保存字节数组,即 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>。AMF 3 使用可变长度编码 29 位整数序列化此类型,其中包括字节长度前缀,然后是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 的原始字节。<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实例的引用发送。</p>\n\n<h4 id=\"15amf3-的使用\">15、AMF3 的使用</h4>\n\n<h5 id=\"151netconnection-and-amf-3\">15.1、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> and AMF 3</h5>\n\n<p>除了序列化 ActionScript 类型外,AMF 还可用于远程服务的异步调用。可使用简单的消息结构将一批请求发送到远程端点。此消息结构的格式为 AMF 0(参见[AMF0])。可以使用特殊的 <code class=\"language-plaintext highlighter-rouge\">avmplus-object-marker</code> 类型将上下文头值或消息正文切换到 AMF 3 编码。</p>\n\n<h5 id=\"152netconnection-in-actionscript-30\">15.2、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> in ActionScript 3.0</h5>\n\n<p>在 ActionScript 3.0 中,NetConnection 的限定类名是 flash.net.NetConnection。这个类仍然使用响应器来处理远程端点的结果和状态响应,但是现在需要强类型的 Responder 类。完全限定的类名是 flash.net.Responder。除了正常的结果和状态响应之外,NetConnection 还会分发事件,开发人员可以添加监听器。下面是这些事件的概述:</p>\n\n<ul>\n <li>当异常异步抛出时触发,例如来自本机异步代码。</li>\n <li>当输入或输出错误导致网络操作失败时触发。</li>\n <li>当 NetConnection 对象报告其状态或错误条件时触发。</li>\n <li>如果对 NetConnection.call() 的调用尝试连接到调用者安全沙箱外的服务器,则会触发。</li>\n</ul>\n\n<h5 id=\"153bytearray-idatainput-and-idataoutput\">15.3、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code>, <code class=\"language-plaintext highlighter-rouge\">IDataInput</code> and <code class=\"language-plaintext highlighter-rouge\">IDataOutput</code></h5>\n\n<p>ActionScript 3.0 引入了一种新类型,用于支持以字节数组形式处理原始数据,即 <code class=\"language-plaintext highlighter-rouge\">flash.utils.ByteArray</code>。为了协助 ActionScript 对象序列化和复制,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实现了 <code class=\"language-plaintext highlighter-rouge\">flash.utils.IDataInput</code> 和 <code class=\"language-plaintext highlighter-rouge\">flash.utils.IDataOutput</code>。这些接口指定了帮助将常见类型写入字节流的实用方法。两个感兴趣的方法是 <code class=\"language-plaintext highlighter-rouge\">IDataOutput.writeObject</code> 和 <code class=\"language-plaintext highlighter-rouge\">IDataInput.readObject</code>。这些方法使用 AMF 编码对象。使用的 AMF 版本由 <code class=\"language-plaintext highlighter-rouge\">ByteArray.objectEncoding</code> 方法控制,该方法可以设置为 AMF 3 或 AMF 0。枚举类型 <code class=\"language-plaintext highlighter-rouge\">flash.net.ObjectEncoding</code> 包含 AMF 版本的常量:分别为 <code class=\"language-plaintext highlighter-rouge\">ObjectEncoding.AMF0</code> 和 <code class=\"language-plaintext highlighter-rouge\">ObjectEncoding.AMF3</code>。</p>\n\n<p>请注意,<code class=\"language-plaintext highlighter-rouge\">ByteArray.writeObject</code> 使用一个版本的 AMF 对整个对象进行编码。与 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 不同,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 不会从 AMF 0 开始,然后将 <code class=\"language-plaintext highlighter-rouge\">objectEncoding</code> 属性设置为 AMF 3 并切换到 AMF 3。还请注意,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 为每个 <code class=\"language-plaintext highlighter-rouge\">readObject</code> 和 <code class=\"language-plaintext highlighter-rouge\">writeObject</code> 调用使用新的对象、对象特征和字符串的隐式引用表。</p>\n\n<h3 id=\"二binaryreaderwriter\">二、<code class=\"language-plaintext highlighter-rouge\">BinaryReader/Writer</code></h3>\n\n<h4 id=\"1amf3-数据格式基础\">1、AMF3 数据格式基础</h4>\n\n<p>首先介绍一下变长整数(Variable Length Integer),比如 UInt32 如下。</p>\n\n<p><img src=\"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png\" alt=\"image\" /></p>\n\n<p>上图摘自 Adobe AMF3 官方文档,这是一种压缩方式的整数存储,且每一字节都对后面的数据具有预知作用。那么字符串如何处理呢?下面是字符串的处理方式,AMF0 和 AMF3 都才用 UTF-8 编码方式,并做如下压缩处理:</p>\n\n<p><img src=\"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png\" alt=\"image\" /></p>\n\n<p>上图摘自 Adobe AMF3 官方文档。</p>\n\n<h4 id=\"2序列化\">2、序列化</h4>\n\n<p>序列化包括 8 位、16 位、32 位,以及 UTF-8 和 UTF-16(I guess)编码的 String,还有原生数据(Raw Data)、变长无符号整数(Variable Length Unsigned Integer)以及 IP 地址。所谓序列化就是按照指定格式编写各种对象、基础数据类型值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">BinaryWriter</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ostream</span><span class=\"o\">&</span> <span class=\"n\">ostr</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">BinaryWriter</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write7BitValue</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write7BitLongValue</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt64</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeAddress</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Address</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">,</span><span class=\"kt\">bool</span> <span class=\"n\">publicFlag</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeAddress</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Net</span><span class=\"o\">::</span><span class=\"n\">SocketAddress</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">,</span><span class=\"kt\">bool</span> <span class=\"n\">publicFlag</span><span class=\"p\">);</span>\n <span class=\"k\">static</span> <span class=\"n\">BinaryWriter</span> <span class=\"n\">BinaryWriterNull</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>请注意其中名为 <code class=\"language-plaintext highlighter-rouge\">BinaryWriterNull</code> 的成员。构造函数定义为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">ostream</span><span class=\"o\">&</span> <span class=\"n\">ostr</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">ostr</span><span class=\"p\">,</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">NETWORK_BYTE_ORDER</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n\n<span class=\"n\">BinaryWriter</span><span class=\"o\">::~</span><span class=\"n\">BinaryWriter</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">writeRaw</code> 是简单地封装 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter::writeRaw()</code>,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入整数实现如下,用的是从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 继承来的重载运算符操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span> \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入字符串:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入变长整数,这段代码含义也一目了然,就是读取变长无符号 32 位整数、64 位整数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write7BitValue</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">Get7BitValueSize</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span><span class=\"o\">*</span><span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">21</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"c1\">// 4 bytes maximum</span>\n <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"mi\">22</span><span class=\"p\">;</span>\n <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">7</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"mh\">0x80</span> <span class=\"o\">|</span> <span class=\"p\">((</span><span class=\"n\">value</span><span class=\"o\">>></span><span class=\"n\">shift</span><span class=\"p\">)</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">));</span>\n <span class=\"n\">shift</span> <span class=\"o\">-=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">max</span> <span class=\"o\">?</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0xFF</span> <span class=\"o\">:</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write7BitLongValue</span><span class=\"p\">(</span><span class=\"n\">UInt64</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">Get7BitValueSize</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span><span class=\"o\">*</span><span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">63</span><span class=\"p\">;</span> <span class=\"c1\">// Can give 10 bytes!</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">max</span><span class=\"p\">)</span>\n <span class=\"o\">++</span><span class=\"n\">shift</span><span class=\"p\">;</span>\n \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">7</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"mh\">0x80</span> <span class=\"o\">|</span> <span class=\"p\">((</span><span class=\"n\">value</span><span class=\"o\">>></span><span class=\"n\">shift</span><span class=\"p\">)</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">));</span>\n <span class=\"n\">shift</span> <span class=\"o\">-=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">max</span> <span class=\"o\">?</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0xFF</span> <span class=\"o\">:</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入 IP 地址的两个函数暂略。</p>\n\n<h4 id=\"3反序列化\">3、反序列化</h4>\n\n<p>反序列化就是从指定格式的数据中读出各类型的数据值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">BinaryReader</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">istream</span><span class=\"o\">&</span> <span class=\"n\">istr</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">BinaryReader</span><span class=\"p\">();</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt64</span> <span class=\"n\">read7BitLongValue</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read7BitEncoded</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString8</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString16</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">read16</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read32</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readAddress</span><span class=\"p\">(</span><span class=\"n\">Address</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">);</span>\n \n <span class=\"k\">static</span> <span class=\"n\">BinaryReader</span> <span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>构造与析构函数都很简单:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">istream</span><span class=\"o\">&</span> <span class=\"n\">istr</span><span class=\"p\">)</span> <span class=\"o\">:</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">istr</span><span class=\"p\">,</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">NETWORK_BYTE_ORDER</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">BinaryReader</span><span class=\"o\">::~</span><span class=\"n\">BinaryReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读取原生数据(Raw Data):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写整数,用的是 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter</code> 的重载运算符:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读写整数依旧使用从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 继承来的运算符操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt8</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read8</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">UInt16</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read16</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">UInt32</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read32</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写字符串:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读取变长整数,分别针对 <code class=\"language-plaintext highlighter-rouge\">UInt32</code> 和 <code class=\"language-plaintext highlighter-rouge\">UInt64</code>,要理解 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 的变长整数才能理解这个:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x80</span><span class=\"p\">)</span> <span class=\"o\">&&</span> <span class=\"n\">n</span> <span class=\"o\"><</span> <span class=\"mi\">3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"p\">(</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"o\">++</span><span class=\"n\">n</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"p\">((</span><span class=\"n\">n</span><span class=\"o\"><</span><span class=\"mi\">3</span><span class=\"p\">)</span> <span class=\"o\">?</span> <span class=\"mi\">7</span> <span class=\"o\">:</span> <span class=\"mi\">8</span><span class=\"p\">);</span> <span class=\"c1\">// Use all 8 bits from the 4th byte</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"n\">b</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt64</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read7BitLongValue</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">UInt64</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x80</span><span class=\"p\">)</span> <span class=\"o\">&&</span> <span class=\"n\">n</span> <span class=\"o\"><</span> <span class=\"mi\">8</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"p\">(</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"o\">++</span><span class=\"n\">n</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"p\">((</span><span class=\"n\">n</span><span class=\"o\"><</span><span class=\"mi\">8</span><span class=\"p\">)</span> <span class=\"o\">?</span> <span class=\"mi\">7</span> <span class=\"o\">:</span> <span class=\"mi\">8</span><span class=\"p\">);</span> <span class=\"c1\">// Use all 8 bits from the 4th byte</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"n\">b</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三packetreaderwriter\">三、<code class=\"language-plaintext highlighter-rouge\">PacketReader/Writer</code></h3>\n\n<h4 id=\"1packetreader\">1、PacketReader</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#define PACKETRECV_SIZE 2048\nclass PacketReader: public BinaryReader {\npublic:\n PacketReader(const Poco::UInt8* buffer,Poco::UInt32 size);\n PacketReader(PacketReader&);\n virtual ~PacketReader();\n const Poco::UInt32 fragments;\n Poco::UInt32 available(); // 可读字节数\n Poco::UInt8* current();\n Poco::UInt32 position(); // 获取当前的相对位置(相对于起始位置的)\n void reset(Poco::UInt32 newPos = 0); // 设定当前位置\n void shrink(Poco::UInt32 rest);\n void next(Poco::UInt32 size);\nprivate:\n MemoryInputStream _memory;\n};\n</code></pre></div></div>\n\n<h6 id=\"11封装-memoryinputstream\">1.1、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">current</code>:当前绝对位置(内存地址)</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code>:当前位置(绝对位置)减去缓冲区起始位置</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"12收缩缓冲区\">1.2、收缩缓冲区</h6>\n\n<p>封装了 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 的 <code class=\"language-plaintext highlighter-rouge\">resize</code>。不过由于前面的 <code class=\"language-plaintext highlighter-rouge\">if</code> 语句影响,传给 resize 的参数一定不会大于缓冲区的当前大小。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">shrink</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">rest</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">rest</span> <span class=\"o\">></span> <span class=\"n\">available</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"rest %u more upper than available %u bytes\"</span><span class=\"p\">,</span><span class=\"n\">rest</span><span class=\"p\">,</span><span class=\"n\">available</span><span class=\"p\">());</span>\n <span class=\"n\">rest</span> <span class=\"o\">=</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">rest</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"13构造函数拷贝构造函数和析构函数\">1.3、构造函数、拷贝构造函数和析构函数</h6>\n\n<p>构造函数先调用父类 <code class=\"language-plaintext highlighter-rouge\">BinaryReader</code> 的构造函数,并初始化 <code class=\"language-plaintext highlighter-rouge\">fragments</code> 和 <code class=\"language-plaintext highlighter-rouge\">_memory</code> 输入流的缓冲区。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">PacketReader</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">fragments</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"c1\">// Consctruction by copy</span>\n<span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">PacketReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">fragments</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">fragments</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">PacketReader</span><span class=\"o\">::~</span><span class=\"n\">PacketReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2packetwriter\">2、<code class=\"language-plaintext highlighter-rouge\">PacketWriter</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">PacketWriter</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">BinaryWriter</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"n\">PacketWriter</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">PacketWriter</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">good</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">clear</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">limit</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"n\">MemoryOutputStream</span> <span class=\"n\">_memory</span><span class=\"p\">;</span>\n <span class=\"n\">PacketWriter</span><span class=\"o\">*</span> <span class=\"n\">_pOther</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h6 id=\"21封装memoryoutputstream\">2.1、封装<code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">good</code>:不过 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code> 也是封装的 <code class=\"language-plaintext highlighter-rouge\">std::ostream</code> 的 <code class=\"language-plaintext highlighter-rouge\">good</code> 函数,True if no error flags are set.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">bool</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">good</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">good</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">written</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">length</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code>:设置缓冲区的指针位置,即 <code class=\"language-plaintext highlighter-rouge\">position</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code>:移动缓冲区指针</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin</code>:返回缓冲区的起始地址</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">clear</code>:其实就是修改 written 和 position,使得指定位置后面的数据在以后写的时候可以被覆盖,并不是真正的清除。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">clear</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">limit</code>:封装 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code> 的 <code class=\"language-plaintext highlighter-rouge\">resize</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">limit</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">length</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">length</span> <span class=\"o\">=</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">length</span> <span class=\"o\">></span> <span class=\"n\">_size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"Limit '%d' more upper than buffer size '%d' bytes\"</span><span class=\"p\">,</span><span class=\"n\">length</span><span class=\"p\">,</span><span class=\"n\">_size</span><span class=\"p\">);</span>\n <span class=\"n\">length</span> <span class=\"o\">=</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">length</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"22封装-binarywriter\">2.2、封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">flush</code>:封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code> 的 <code class=\"language-plaintext highlighter-rouge\">flush</code>,不过 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code> 的 <code class=\"language-plaintext highlighter-rouge\">flush</code> 实际上是从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter</code> 继承而来的。其作用是「Flushes the underlying stream」。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">flush</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_pOther</span> <span class=\"o\">&&</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"n\">_pOther</span><span class=\"o\">-></span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">())</span>\n <span class=\"n\">_pOther</span><span class=\"o\">-></span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">());</span>\n <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">flush</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"23构造函数拷贝构造函数和析构函数\">2.3、构造函数、拷贝构造函数和析构函数</h6>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">_pOther</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_size</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"c1\">// Consctruction by copy</span>\n<span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"n\">PacketWriter</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_pOther</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">other</span><span class=\"p\">),</span>\n <span class=\"n\">_memory</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">_size</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>注意析构函数中会进行 <code class=\"language-plaintext highlighter-rouge\">flush</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketWriter</span><span class=\"o\">::~</span><span class=\"n\">PacketWriter</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"四amfreader\">四、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code></h3>\n\n<h4 id=\"1objectdef\">1、<code class=\"language-plaintext highlighter-rouge\">ObjectDef</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ObjectDef</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span> \n <span class=\"n\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">amf3</span><span class=\"p\">(</span><span class=\"n\">amf3</span><span class=\"p\">),</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">dynamic</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">externalizable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">count</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">arrayType</span><span class=\"p\">(</span><span class=\"n\">arrayType</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">string</span><span class=\"o\">></span> <span class=\"n\">hardProperties</span><span class=\"p\">;</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">reset</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">dynamic</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">externalizable</span><span class=\"p\">;</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">count</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"p\">;</span>\n <span class=\"k\">const</span> <span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"2amfreader-定义\">2、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 定义</h4>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 作为其成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">AMFReader</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">AMFReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">AMFReader</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">readSimpleObject</span><span class=\"p\">(</span><span class=\"n\">AMFSimpleObject</span><span class=\"o\">&</span> <span class=\"n\">object</span><span class=\"p\">);</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">read</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">double</span> <span class=\"n\">readNumber</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Int32</span> <span class=\"n\">readInteger</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readBoolean</span><span class=\"p\">();</span>\n <span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">readByteArray</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">&</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Timestamp</span> <span class=\"n\">readDate</span><span class=\"p\">();</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">readObject</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readArray</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readDictionary</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">weakKeys</span><span class=\"p\">);</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readKey</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readValue</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readItem</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">);</span>\n <span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">readRawObjectContent</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">readNull</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">startReferencing</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">stopReferencing</span><span class=\"p\">();</span>\n \n <span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">;</span>\n \n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">ObjectDef</span><span class=\"o\">*></span> <span class=\"n\">_objectDefs</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_stringReferences</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_classDefReferences</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_amf0References</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_amf0Reset</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_reset</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_amf3</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">_referencing</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"21构造函数析构函数\">2.1、构造函数、析构函数</h5>\n\n<p>参数为 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code>,会初始化一些成员变量。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">AMFReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">reader</span><span class=\"p\">(</span><span class=\"n\">reader</span><span class=\"p\">),</span>\n <span class=\"n\">_reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_amf3</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_amf0Reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_referencing</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构时,会逐一释放 <code class=\"language-plaintext highlighter-rouge\">_objectDefs</code> 中对象的内存:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMFReader</span><span class=\"o\">::~</span><span class=\"n\">AMFReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">ObjectDef</span><span class=\"o\">*>::</span><span class=\"n\">iterator</span> <span class=\"n\">it</span><span class=\"p\">;</span>\n <span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span>\n <span class=\"k\">delete</span> <span class=\"o\">*</span><span class=\"n\">it</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22简单封装-packetreader-的一些函数\">2.2、简单封装 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 的一些函数</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code>:操作指针位置</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_reset</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"p\">);</span>\n <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code>:根据当前缓冲区大小和 <code class=\"language-plaintext highlighter-rouge\">written</code> 计算得到</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">current</code>:<code class=\"language-plaintext highlighter-rouge\">gptr</code> 内存地址</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">*</span><span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23设置-gptr-位置\">2.3、设置 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 位置</h5>\n\n<p>其实 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 也被影响了,但是在 <code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 中只用 <code class=\"language-plaintext highlighter-rouge\">gptr</code>。调用构造函数的时候,<code class=\"language-plaintext highlighter-rouge\">reset</code> 被设为 0,其后在每次读取数据的时候都会影响 <code class=\"language-plaintext highlighter-rouge\">reset</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"p\">);</span>\n <span class=\"n\">_reset</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"24判断类型\">2.4、判断类型</h5>\n\n<p>分析请看注释:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">followingType</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先 <code class=\"language-plaintext highlighter-rouge\">reset</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span> <span class=\"o\">!=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">back</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">amf3</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>是 AMF0 类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>如果没有可读数据了,则返回 AMF::End。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">available</span><span class=\"p\">())</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">End</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>开始读了,先读到的表示 AMF 数据类型。要注意的是调用 current 并不改变指针的位置,所以你会在线面看到调用 next。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt8</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_amf3</span> <span class=\"o\">&&</span> <span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF_AVMPLUS_OBJECT</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">available</span><span class=\"p\">())</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">End</span><span class=\"p\">;</span>\n <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>AMF3 类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">switch</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>Undefined 和 null 都当做 null。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF3_UNDEFINED</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_NULL</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>false 和 true 都是 boolean。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF3_FALSE</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_TRUE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_INTEGER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Integer</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_NUMBER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">String</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_DATE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Array</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_DICTIONARY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Dictionary</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Object</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_BYTEARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">ByteArray</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>落到 default 手里的话,就跳过这个字节,读取下一个。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"nl\">default:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Unknown AMF3 type %.2x\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>AMF0 类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">switch</span> <span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">undefined</code> 和 <code class=\"language-plaintext highlighter-rouge\">null</code> 都是 <code class=\"language-plaintext highlighter-rouge\">null</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_UNDEFINED</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_NULL</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">;</span>\n \n <span class=\"k\">case</span> <span class=\"n\">AMF_BOOLEAN</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_NUMBER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">long string</code> 和 <code class=\"language-plaintext highlighter-rouge\">string</code> 都是 <code class=\"language-plaintext highlighter-rouge\">string</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_LONG_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">String</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">mixed array</code> 和 <code class=\"language-plaintext highlighter-rouge\">strict array</code> 都是 <code class=\"language-plaintext highlighter-rouge\">array</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_MIXED_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_STRICT_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Array</span><span class=\"p\">;</span>\n \n <span class=\"k\">case</span> <span class=\"n\">AMF_DATE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin object</code> 和 <code class=\"language-plaintext highlighter-rouge\">begin typed object</code> 都是 <code class=\"language-plaintext highlighter-rouge\">object</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_BEGIN_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_BEGIN_TYPED_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Object</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是引用,就跳过表示类型值的这个字节。这个先留下来,带我们分析完 readArray 和 readObject 再回头看。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_REFERENCE</span><span class=\"p\">:</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read16</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">reference</span> <span class=\"o\">></span> <span class=\"n\">_amf0References</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF0 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_amf0Reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_amf0References</span><span class=\"p\">[</span><span class=\"n\">reference</span><span class=\"p\">]);</span>\n <span class=\"k\">return</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果没了,或者不支持,或者都不是,就跳过这个字节,递归继续读取:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_END_OBJECT</span><span class=\"p\">:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF end object type without begin object type before\"</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_UNSUPPORTED</span><span class=\"p\">:</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"Unsupported type in AMF format\"</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">default</span><span class=\"o\">:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Unknown AMF type %.2x\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">followingType</code> 是这个类的核心,每个具体的数据类型的分析都依赖于它的判断。这些类型的解析,会在下一篇文章中介绍。</p>\n\n<h4 id=\"3解析-as3-null\">3、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Null</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readNull</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先 reset 一下是惯例,就像糗百上的割一样。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span> \n</code></pre></div></div>\n\n<p>AMF 数据类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">Null</code>,跳过该字节,并返回</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>判断错误</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Null type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4解析-as3-number\">4、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Number</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">double</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readNumber</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Null</code> 会被悲催的跳过:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">Number</code> 呀 :(</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Number type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过该字节吧</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>返回吧,返回之前还用到 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 的运算符,注意 AS3 中的 <code class=\"language-plaintext highlighter-rouge\">Number</code> 就是 C++ 的 <code class=\"language-plaintext highlighter-rouge\">double</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">double</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5解析-as3-integer\">5、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Integer</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Int32</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readInteger</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code> 类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>Null 的话:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">Integer</code> 或者 Number 的话。。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Integer</span> <span class=\"o\">&&</span> <span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Integer type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过吧。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>终于是 <code class=\"language-plaintext highlighter-rouge\">Number</code> 了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kt\">double</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Int32</span><span class=\"p\">)</span><span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读一个变长的 32 位无符号整数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Forced in AMF3 here!</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">value</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果大于 3.5 个字节所能表示的最大无符号整数值(<code class=\"language-plaintext highlighter-rouge\">268435455</code> 是 <code class=\"language-plaintext highlighter-rouge\">0xFFFFFFF</code>),则减去 <code class=\"language-plaintext highlighter-rouge\">0x2FFFFFFF</code>(这还不是太理解,有能解释的朋友给留个言,或者发 email 给我 )</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">></span> <span class=\"mi\">268435455</span><span class=\"p\">)</span>\n <span class=\"n\">value</span> <span class=\"o\">-=</span> <span class=\"p\">(</span><span class=\"mi\">1</span> <span class=\"o\"><<</span> <span class=\"mi\">29</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"6解析-as3-boolean\">6、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Boolean</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readBoolean</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">Null</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>居然不是 <code class=\"language-plaintext highlighter-rouge\">Boolean</code> 的话。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Boolean type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 的话,返回 <code class=\"language-plaintext highlighter-rouge\">true</code> 或者 <code class=\"language-plaintext highlighter-rouge\">false</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span><span class=\"o\">==</span> <span class=\"n\">AMF3_FALSE</span> <span class=\"o\">?</span> <span class=\"nb\">false</span> <span class=\"o\">:</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 就是 <code class=\"language-plaintext highlighter-rouge\">AMF0</code> 喽:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span><span class=\"o\">==</span><span class=\"mh\">0x00</span> <span class=\"o\">?</span> <span class=\"nb\">false</span> <span class=\"o\">:</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"7开始引用与结束引用\">7、开始引用与结束引用</h4>\n\n<p>如下这两个函数会在 <code class=\"language-plaintext highlighter-rouge\">FlowConnection</code> 中调用。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">startReferencing</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_referencing</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">stopReferencing</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_referencing</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"8解析-as3-bytearray\">8、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code></h4>\n\n<p>先回顾一下 AMF3 中的ByteArray 的数据格式:</p>\n\n<p>注意到,首先要读取一个变长无符号 32 位整数,但是最低位是 1,只有 28 位用于表示数据长度。解释完这里,下面的解析过程才好理解。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readByteArray</span><span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Null</code> 就返回 <code class=\"language-plaintext highlighter-rouge\">BinaryReaderNull</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>,也返回 <code class=\"language-plaintext highlighter-rouge\">BinaryReaderNull</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">ByteArray</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF ByteArray type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过这个字节:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>注意 position 返回的是相对位置,与 AS3 中一样。<code class=\"language-plaintext highlighter-rouge\">reference</code> 表示这个地址(简单说,引用就是地址嘛)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>读取一个变长 32 位无符号整数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>最低位是 1 的话,<code class=\"language-plaintext highlighter-rouge\">isInline</code> 是 <code class=\"language-plaintext highlighter-rouge\">true</code>,否则为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">size</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>右移一位,因为那一位是标志位,上面解释过了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">isInline</code> 是 <code class=\"language-plaintext highlighter-rouge\">true</code>,表示是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">_referencing</code> 为 <code class=\"language-plaintext highlighter-rouge\">true</code> 的话(这是一个 <code class=\"language-plaintext highlighter-rouge\">vector</code>),push back this reference:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>不符合 ByteArray 的格式定义的话:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">size</span> <span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>移动到这个 reference 的位置,<code class=\"language-plaintext highlighter-rouge\">_references[size]</code> 就是这个位置(相对)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">size</span><span class=\"p\">]);</span> <span class=\"c1\">// TODO size 作为索引,还没有完全理解</span>\n</code></pre></div></div>\n\n<p>读取这个 reference 的 size 值给 size对象(注意 size 是这个函数传入的引用参数,其值可以被修改)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"o\">>></span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>把读取完 ByteArraty 的 PacketReader 返回:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>最后强调一点,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 的数据段最大长度为 228 -1 字节,约为 256 MB。</p>\n\n<h4 id=\"9解析-as3-date\">9、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Date</code></h4>\n\n<p>先看下 <code class=\"language-plaintext highlighter-rouge\">Date</code> 的数据格式:</p>\n\n<p>下面开始分析:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Timestamp</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readDate</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>Null 的话,就返回当前时间:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是 Date 类型,也返回当前时间:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Date type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"kt\">double</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是 AMF3:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先读取 flag,最低一位必须是 1,其他位丢到垃圾桶。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">flags</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>当前相对位置。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>是 1 就 push back 到 <code class=\"language-plaintext highlighter-rouge\">_references</code> 里。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">flags</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>读取一个 double,到 result 里(result 也是 double 类型哦~)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>如果标志位不是 1,麻烦不少哒。。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">flags</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果 flag 超了,就返回当前时间作为时间戳作为 Date。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">flags</span> <span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这段与 ByteArray 那段一样:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">flags</span><span class=\"p\">]);</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>返回喽~</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"nf\">Timestamp</span><span class=\"p\">((</span><span class=\"n\">Timestamp</span><span class=\"o\">::</span><span class=\"n\">TimeVal</span><span class=\"p\">)</span> <span class=\"n\">result</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>读俩,因为是 double(64 位):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">2</span><span class=\"p\">);</span> <span class=\"c1\">// Timezone, useless</span>\n</code></pre></div></div>\n\n<p>返回喽~</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"nf\">Timestamp</span><span class=\"p\">((</span><span class=\"n\">Timestamp</span><span class=\"o\">::</span><span class=\"n\">TimeVal</span><span class=\"p\">)</span> <span class=\"n\">result</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"10解析-as3-dictionary\">10、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Dictionary</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readDictionary</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">weakKeys</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>下面这段咱就略了。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Dictionary</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Dictionary type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过 type:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// AMF3</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span> <span class=\"c1\">// marker</span>\n</code></pre></div></div>\n\n<p>当前相对位置值作为 <code class=\"language-plaintext highlighter-rouge\">reference</code>,再读个 <code class=\"language-plaintext highlighter-rouge\">size</code>,还是最低位必须为 1,不是就返回 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">size</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n <span class=\"n\">size</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">isInline</span> <span class=\"o\">&&</span> <span class=\"n\">size</span><span class=\"o\">></span><span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>下面要调用到 <code class=\"language-plaintext highlighter-rouge\">ObjectRef</code> 构造函数,这里再把其实现拿出来看看,其实主要是初始化了哪些成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">amf3</span><span class=\"p\">(</span><span class=\"n\">amf3</span><span class=\"p\">),</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">dynamic</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">externalizable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">count</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">arrayType</span><span class=\"p\">(</span><span class=\"n\">arrayType</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>可以看到要有一个 amf3,还有 <code class=\"language-plaintext highlighter-rouge\">reset</code> 置为 0,<code class=\"language-plaintext highlighter-rouge\">dynamic</code> 置为 <code class=\"language-plaintext highlighter-rouge\">false</code>,<code class=\"language-plaintext highlighter-rouge\">externalizable</code> 也是 <code class=\"language-plaintext highlighter-rouge\">false</code>,<code class=\"language-plaintext highlighter-rouge\">count</code> 是 0,<code class=\"language-plaintext highlighter-rouge\">arrayType</code> 成员要赋值。</p>\n\n<p>上面是插播哦,下面还要继续哒。创建这么一个对象,注意是 new 出来的,所以我们在《OpenRTMFP/Cumulus Primer(16)AMF解析之AMFReader》一文中提到了 AMFReader 的析构函数中要对 <code class=\"language-plaintext highlighter-rouge\">_objectRef</code> 的每个元素逐一析构的。<code class=\"language-plaintext highlighter-rouge\">arrayType</code> 就设置为 <code class=\"language-plaintext highlighter-rouge\">AMF3_DICTIONARY</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ObjectDef</span><span class=\"o\">*</span> <span class=\"n\">pObjectDef</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nf\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">,</span> <span class=\"n\">AMF3_DICTIONARY</span><span class=\"p\">);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">dynamic</span><span class=\"o\">=</span><span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pObjectDef</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果标志位是 1,就直接 push back,跟之前一样。不过这里多了一个 <code class=\"language-plaintext highlighter-rouge\">pObjectDef</code>,所以还要设置一下它的计数为 <code class=\"language-plaintext highlighter-rouge\">size</code>,就是 <code class=\"language-plaintext highlighter-rouge\">dictionary</code> 数据大小。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">=</span> <span class=\"n\">size</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果标志位是 0,就把 <code class=\"language-plaintext highlighter-rouge\">count</code> 设置为下一个变长整数值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">size</span><span class=\"p\">]);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"o\">>></span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">*=</span> <span class=\"mi\">2</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>读一个字节,如果最小位是 1,weakKeys 就是 true,否则为 false。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">weakKeys</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n \n <span class=\"k\">return</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</title>\n \t<meta name=\"description\" content=\"CumulusServer 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 CumulusEdge 和 CumulusServer 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 CumulusServer 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</h2>\t\t\n\t<time datetime=\"2012-04-15T14:26:58+00:00\" class=\"by-line\">15 Apr 2012, 广州 | 作者 麦克船长 | 总计 3844 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#1绑定地址\" id=\"markdown-toc-1绑定地址\">1、绑定地址</a></li>\n <li><a href=\"#2cumulusserver-接收数据\" id=\"markdown-toc-2cumulusserver-接收数据\">2、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 接收数据</a></li>\n <li><a href=\"#3如果-cumulusedge-端口存在且-edge-socket-可用\" id=\"markdown-toc-3如果-cumulusedge-端口存在且-edge-socket-可用\">3、如果 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 端口存在且 edge socket 可用。</a></li>\n <li><a href=\"#4cumulusserver-和-cumulusedge-的-socket-都没有数据\" id=\"markdown-toc-4cumulusserver-和-cumulusedge-的-socket-都没有数据\">4、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 都没有数据:</a></li>\n <li><a href=\"#5发送方的-ip-被禁\" id=\"markdown-toc-5发送方的-ip-被禁\">5、发送方的 ip 被禁:</a></li>\n <li><a href=\"#6数据包长度小于可能的最小值12\" id=\"markdown-toc-6数据包长度小于可能的最小值12\">6、数据包长度小于可能的最小值(12)</a></li>\n</ul>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。</p>\n\n<p>本所要介绍的这个主循环在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(const volatile bool& terminate)</code> 函数中。RTMFPServer覆盖 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的 <code class=\"language-plaintext highlighter-rouge\">run(const volatile bool &terminate)</code> 方法。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<h3 id=\"1绑定地址\">1、绑定地址</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 IP 地址和端口:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"nf\">address</span><span class=\"p\">(</span><span class=\"s\">\"0.0.0.0\"</span><span class=\"p\">,</span><span class=\"n\">_port</span><span class=\"p\">);</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">address</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n<span class=\"err\">绑定</span><span class=\"n\">CumulusEdge</span><span class=\"err\">的</span> <span class=\"n\">IP</span> <span class=\"err\">地址和端口:</span>\n\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"nf\">edgesAddress</span><span class=\"p\">(</span><span class=\"s\">\"0.0.0.0\"</span><span class=\"p\">,</span><span class=\"n\">_edgesPort</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">edgesAddress</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>发送者(Client)的 IP 地址和端口:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"n\">sender</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">buff</span><span class=\"p\">[</span><span class=\"n\">PACKETRECV_SIZE</span><span class=\"p\">];</span>\n <span class=\"kt\">int</span> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n \n <span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">stop</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">idle</span> <span class=\"o\">=</span> <span class=\"n\">realTime</span><span class=\"p\">(</span><span class=\"n\">stop</span><span class=\"p\">);</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">stop</span><span class=\"p\">)</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n \n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h3 id=\"2cumulusserver-接收数据\">2、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 接收数据</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 有数据可读:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>从 <code class=\"language-plaintext highlighter-rouge\">socket</code> 读取:把数据存到 <code class=\"language-plaintext highlighter-rouge\">buff</code>,把发送者地址赋给 <code class=\"language-plaintext highlighter-rouge\">sender</code>,把所读长度返回给 <code class=\"language-plaintext highlighter-rouge\">size</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">receiveFrom</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span><span class=\"k\">sizeof</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">),</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>处理 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 产生的异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Main socket reception : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">address</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"3如果-cumulusedge-端口存在且-edge-socket-可用\">3、如果 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 端口存在且 edge socket 可用。</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 有数据可读:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span> <span class=\"o\">></span> <span class=\"mi\">0</span> <span class=\"o\">&&</span> <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">receiveFrom</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span> <span class=\"k\">sizeof</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">),</span> <span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Main socket reception : %s\"</span><span class=\"p\">,</span> <span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">edgesAddress</span><span class=\"p\">,</span> <span class=\"nb\">true</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">Edge</span><span class=\"o\">*</span> <span class=\"n\">pEdge</span> <span class=\"o\">=</span> <span class=\"n\">edges</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pEdge</span><span class=\"p\">)</span>\n <span class=\"n\">pEdge</span><span class=\"o\">-></span><span class=\"n\">update</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<h3 id=\"4cumulusserver-和-cumulusedge-的-socket-都没有数据\">4、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 都没有数据:</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 空闲:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">idle</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>主线程等待一秒。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_timeLastManage</span><span class=\"p\">.</span><span class=\"n\">isElapsed</span><span class=\"p\">(</span><span class=\"n\">_freqManage</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>Just middle session</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_middle</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Sessions</span><span class=\"o\">::</span><span class=\"n\">Iterator</span> <span class=\"n\">it</span><span class=\"p\">;</span>\n <span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span> <span class=\"o\">!=</span> <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Middle</span><span class=\"o\">*</span> <span class=\"n\">pMiddle</span> <span class=\"o\">=</span> <span class=\"k\">dynamic_cast</span><span class=\"o\"><</span><span class=\"n\">Middle</span><span class=\"o\">*></span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMiddle</span><span class=\"p\">)</span>\n <span class=\"n\">pMiddle</span><span class=\"o\">-></span><span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_timeLastManage</span><span class=\"p\">.</span><span class=\"n\">update</span><span class=\"p\">();</span>\n <span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"5发送方的-ip-被禁\">5、发送方的 ip 被禁:</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isBanned</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">.</span><span class=\"n\">host</span><span class=\"p\">()))</span> <span class=\"p\">{</span>\n <span class=\"n\">INFO</span><span class=\"p\">(</span><span class=\"s\">\"Data rejected because client %s is banned\"</span><span class=\"p\">,</span>\n <span class=\"n\">sender</span><span class=\"p\">.</span><span class=\"n\">host</span><span class=\"p\">().</span><span class=\"n\">toString</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"6数据包长度小于可能的最小值12\">6、数据包长度小于可能的最小值(12)</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">size</span> <span class=\"o\"><</span> <span class=\"n\">RTMFP_MIN_PACKET_SIZE</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Invalid packet\"</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">PacketReader</span> <span class=\"nf\">packet</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Session</span><span class=\"o\">*</span> <span class=\"n\">pSession</span> <span class=\"o\">=</span> <span class=\"n\">findSession</span><span class=\"p\">(</span><span class=\"n\">RTMFP</span><span class=\"o\">::</span><span class=\"n\">Unpack</span><span class=\"p\">(</span><span class=\"n\">packet</span><span class=\"p\">));</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">pSession</span><span class=\"p\">)</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">checked</span><span class=\"p\">)</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">commitCookie</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pSession</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>给 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 或者自己(<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>)的 <code class=\"language-plaintext highlighter-rouge\">socket</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">setEndPoint</span><span class=\"p\">(</span><span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span> <span class=\"o\">?</span> <span class=\"n\">_edgesSocket</span> <span class=\"o\">:</span> <span class=\"n\">_socket</span><span class=\"p\">,</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">receive</span><span class=\"p\">(</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_pCirrus</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">delete</span> <span class=\"n\">_pCirrus</span><span class=\"p\">;</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"nb\">NULL</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的第二篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</h2>\t\t\n\t<time datetime=\"2012-04-14T11:20:46+00:00\" class=\"by-line\">14 Apr 2012, 广州 | 作者 麦克船长 | 总计 18890 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一cumulus-启动源码分析\" id=\"markdown-toc-一cumulus-启动源码分析\">一、Cumulus 启动源码分析</a> <ul>\n <li><a href=\"#1maincpp-中的-main-函数\" id=\"markdown-toc-1maincpp-中的-main-函数\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数</a></li>\n <li><a href=\"#2maincpp-中的-cumulusserver-构造函数\" id=\"markdown-toc-2maincpp-中的-cumulusserver-构造函数\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer()</code> 构造函数</a></li>\n <li><a href=\"#3maincpp-中的-cumulusserver-的-initialize-成员函数\" id=\"markdown-toc-3maincpp-中的-cumulusserver-的-initialize-成员函数\">3、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 成员函数</a></li>\n <li><a href=\"#4maincpp-中的-cumulusserver-的-main-成员函数\" id=\"markdown-toc-4maincpp-中的-cumulusserver-的-main-成员函数\">4、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 成员函数</a></li>\n <li><a href=\"#5cumulusserver-是-serverapplication-的子类\" id=\"markdown-toc-5cumulusserver-是-serverapplication-的子类\">5、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的子类</a></li>\n <li><a href=\"#6serverapplication-是-application-的子类\" id=\"markdown-toc-6serverapplication-是-application-的子类\">6、<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的子类</a></li>\n <li><a href=\"#7反初始化\" id=\"markdown-toc-7反初始化\">7、反初始化</a></li>\n </ul>\n </li>\n <li><a href=\"#二cumulusserver-各项交互功能的源码解读\" id=\"markdown-toc-二cumulusserver-各项交互功能的源码解读\">二、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 各项交互功能的源码解读</a> <ul>\n <li><a href=\"#1命令行选项设定\" id=\"markdown-toc-1命令行选项设定\">1、命令行选项设定</a></li>\n <li><a href=\"#2处理命令行选项\" id=\"markdown-toc-2处理命令行选项\">2、处理命令行选项</a></li>\n <li><a href=\"#6dump-logs\" id=\"markdown-toc-6dump-logs\">6、Dump logs</a></li>\n <li><a href=\"#3停止运行\" id=\"markdown-toc-3停止运行\">3、停止运行</a></li>\n <li><a href=\"#4载入配置\" id=\"markdown-toc-4载入配置\">4、载入配置</a></li>\n <li><a href=\"#5处理日志\" id=\"markdown-toc-5处理日志\">5、处理日志</a></li>\n </ul>\n </li>\n <li><a href=\"#三maincpp-的-main-函数源码分析\" id=\"markdown-toc-三maincpp-的-main-函数源码分析\">三、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数源码分析</a> <ul>\n <li><a href=\"#1maincpp-中的-main-函数中的-server\" id=\"markdown-toc-1maincpp-中的-main-函数中的-server\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中的 <code class=\"language-plaintext highlighter-rouge\">server</code></a></li>\n <li><a href=\"#2maincpp-中-main-函数的-serverstart\" id=\"markdown-toc-2maincpp-中-main-函数的-serverstart\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的 <code class=\"language-plaintext highlighter-rouge\">server.start()</code></a></li>\n <li><a href=\"#3回顾一下整个启动流程\" id=\"markdown-toc-3回顾一下整个启动流程\">3、回顾一下整个启动流程</a></li>\n <li><a href=\"#4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\" id=\"markdown-toc-4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>、<code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(...)</code> 函数源码</a></li>\n </ul>\n </li>\n</ul>\n\n<p>本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。首先要知道的是,OpenRTMFP/Cumulus 中使用到的库有 Poco、OpenSSL 和 Lua。</p>\n\n<h3 id=\"一cumulus-启动源码分析\">一、Cumulus 启动源码分析</h3>\n\n<h4 id=\"1maincpp-中的-main-函数\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数</h4>\n\n<p>入口在 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"kt\">int</span> <span class=\"n\">argc</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">argv</span><span class=\"p\">[])</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先检查内存泄露,不过目前这个开发中的项目还没有实现这个功能,只是个空函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">DetectMemoryLeak</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>然后会创建一个匿名的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 对象,并调用其 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,该函数由 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 从 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 中继承而来,而 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 由是从 <code class=\"language-plaintext highlighter-rouge\">Application</code> 继承而来的。<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 对象调用 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,实际是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数则是调用 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的函数,而该 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数则是先调用 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数,然后调用 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,然后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数。如果 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数被调用时抛出异常,则不会执行 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,但仍然会执行 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Runs the application by performing additional initializations</span>\n <span class=\"c1\">// and calling the main() method.</span>\n <span class=\"k\">return</span> <span class=\"nf\">CumulusServer</span><span class=\"p\">().</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"n\">argc</span><span class=\"p\">,</span> <span class=\"n\">argv</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2maincpp-中的-cumulusserver-构造函数\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer()</code> 构造函数</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的构造函数定义为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">CumulusServer</span><span class=\"p\">()</span><span class=\"o\">:</span> <span class=\"n\">_helpRequested</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span> <span class=\"c1\">// 显示帮助信息</span>\n <span class=\"n\">_pCirrus</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_middle</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">_isInteractive</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_pLogFile</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3maincpp-中的-cumulusserver-的-initialize-成员函数\">3、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 成员函数</h4>\n\n<p>在执行 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数之前,<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 会启动 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数,传入的参数就是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 自己,可以猜到 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> 的 <code class=\"language-plaintext highlighter-rouge\">run</code> 方法中,调用该函数时的参数是 <code class=\"language-plaintext highlighter-rouge\">this</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">initialize</span><span class=\"p\">(</span><span class=\"n\">Application</span><span class=\"o\">&</span> <span class=\"n\">self</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>调用父函数 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的初始化函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">initialize</span><span class=\"p\">(</span><span class=\"n\">self</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>再继续下面的源码分析之前,先要知道,根据 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> ,<code class=\"language-plaintext highlighter-rouge\">Application</code> 类有一些内置的配置属性,如下:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">application.path</code>: 可执行文件的绝对路径;</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.name</code>: 可执行文件的文件名(含扩展名);</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.baseName</code>: 可执行文件的文件名(不含扩展名)</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.dir</code>: 可执行文件的所在目录;</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.configDir</code>: 配置文件所在目录;</li>\n</ul>\n\n<p>所以下面就读取了可执行文件的所在目录,其中第二个参数表示默认值(即当前目录):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">string</span> <span class=\"n\">dir</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span> <span class=\"s\">\"./\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>然后读取配置文件,目录为上一句所得到的 <code class=\"language-plaintext highlighter-rouge\">dir</code>,文件名(不含扩展名)为 <code class=\"language-plaintext highlighter-rouge\">application.basename</code> 内置配置属性值,其默认值为 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>,然后加上点和扩展名 <code class=\"language-plaintext highlighter-rouge\">.ini</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">loadConfiguration</span><span class=\"p\">(</span><span class=\"n\">dir</span>\n <span class=\"o\">+</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.baseName\"</span><span class=\"p\">,</span> <span class=\"s\">\"CumulusServer\"</span><span class=\"p\">)</span>\n <span class=\"o\">+</span> <span class=\"s\">\".ini\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这样就加载完配置了。然后查看当前的进程是从命令行运行的(命令行是交互的,所以是 interactive),还是以 daemon 方式运行的,这个函数是ServerApplication的一个成员函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_isInteractive</span> <span class=\"o\">=</span> <span class=\"n\">isInteractive</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>然后获取表示日志文件所在目录的字符串,其中 <code class=\"language-plaintext highlighter-rouge\">logs.directory</code> 是外置配置属性(配置文件中),其默认值为上面获取到的可执行文件路径(一般为当前路径)与 <code class=\"language-plaintext highlighter-rouge\">logs</code> 的组合,即一般为当前目录下的 <code class=\"language-plaintext highlighter-rouge\">logs</code> 目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">string</span> <span class=\"nf\">logDir</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"logs.directory\"</span><span class=\"p\">,</span> <span class=\"n\">dir</span> <span class=\"o\">+</span> <span class=\"s\">\"logs\"</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>创建日志文件目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span><span class=\"p\">(</span><span class=\"n\">logDir</span><span class=\"p\">).</span><span class=\"n\">createDirectory</span><span class=\"p\">();</span>\n\n</code></pre></div></div>\n\n<p>日志文件绝对路径,<code class=\"language-plaintext highlighter-rouge\">logs</code> 为默认的日志文件名(不含扩展名的部分),扩展名用0:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logPath</span> <span class=\"o\">=</span> <span class=\"n\">logDir</span> <span class=\"o\">+</span> <span class=\"s\">\"/\"</span> <span class=\"o\">+</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"logs.name\"</span><span class=\"p\">,</span> <span class=\"s\">\"log\"</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"s\">\".\"</span><span class=\"p\">;</span>\n <span class=\"n\">_pLogFile</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nf\">File</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"s\">\"0\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>用日志流打开日志文件(方式为追加写入):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">open</span><span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">path</span><span class=\"p\">(),</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">in</span> <span class=\"o\">|</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">ate</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Logs</code> 是一个方法类(其中的 <code class=\"language-plaintext highlighter-rouge\">public</code> 函数都是静态的),<code class=\"language-plaintext highlighter-rouge\">SetLogger</code> 的作用就是将Logs中的似有静态成员设置为某个 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Logger</code> 对象(由于 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 继承了 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Logger</code>)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetLogger</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4maincpp-中的-cumulusserver-的-main-成员函数\">4、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 成员函数</h4>\n\n<p>OpenRTMFP/Cumulus 是一个基于 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> 的服务端应用(准确的说是基于 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::ServerApplication</code> 的服务端应用)。如果没有特殊的启动要求,可以调用 <code class=\"language-plaintext highlighter-rouge\">Poco/Application.h</code> 中定义的宏 <code class=\"language-plaintext highlighter-rouge\">POCO_APP_MAIN</code> 来完成初始化、日志和启动(该宏会根据不同的平台启用不同的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数)。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">run()</code> 函数在调用完 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数后,会调用 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,该 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的定义在 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">>&</span> <span class=\"n\">args</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>首先看是否是要求帮助信息,<code class=\"language-plaintext highlighter-rouge\">displayHelp</code> 是借助 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::HelpFormatter</code> 实现的,<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的构造函数会在调用时将 <code class=\"language-plaintext highlighter-rouge\">_helpRequested</code> 设置为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_helpRequested</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">displayHelp</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是,则进入启动状态,首先创建一个 <code class=\"language-plaintext highlighter-rouge\">RTMFPServerParams</code> 对象 <code class=\"language-plaintext highlighter-rouge\">params</code>,用来存储 OpenRTMFP/CumulusServer 的基本配置信息。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">RTMFPServerParams</span> <span class=\"n\">params</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">params</code> 存储 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的端口号和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的端口号:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"port\"</span><span class=\"p\">,</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span><span class=\"p\">);</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">edgesPort</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"edges.port\"</span><span class=\"p\">,</span>\n <span class=\"n\">RTMFP_DEFAULT_PORT</span><span class=\"o\">+</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getBool</span><span class=\"p\">(</span><span class=\"s\">\"edges.activated\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">edgesPort</span><span class=\"o\">==</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"edges.port must have a positive value if \\\n edges.activated is true. Server edges is \\\n disactivated.\"</span><span class=\"p\">);</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesPort</span><span class=\"o\">=</span><span class=\"n\">edgesPort</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">_pCirrus</code> 为 <code class=\"language-plaintext highlighter-rouge\">SocketAddress</code> 的成员,是封装IP地址和端口号的对象。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span> <span class=\"o\">=</span> <span class=\"n\">_pCirrus</span><span class=\"p\">;</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">middle</span> <span class=\"o\">=</span> <span class=\"n\">_middle</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>UDB 所使用的缓冲区大小:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"udpBufferSize\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span>\n <span class=\"s\">\"keepAliveServer\"</span><span class=\"p\">,</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"keepAlivePeer\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>失败前 CumulusEdge 的尝试次数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span>\n <span class=\"s\">\"edges.attemptsBeforeFallback\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Server</span> <span class=\"nf\">server</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span><span class=\"s\">\"./\"</span><span class=\"p\">),</span>\n <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span><span class=\"n\">config</span><span class=\"p\">());</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">waitForTerminationRequest()</code> 函数是 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中必须调用的,意为等待终止运行的操作请求。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// wait for CTRL-C or kill</span>\n <span class=\"n\">waitForTerminationRequest</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>一旦接收到终止操作的请求,就会执行下面这句,用以退出 OpenRTMFP/Cumulus 的运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Stop the server</span>\n <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">stop</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">catch</code> 一些可能产生的异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"Configuration problem : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">what</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer unknown error\"</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>OpenRTMFP/CumulusServer 停止运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"n\">Application</span><span class=\"o\">::</span><span class=\"n\">EXIT_OK</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5cumulusserver-是-serverapplication-的子类\">5、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的子类</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 对其子类有如下要求:</p>\n\n<ul>\n <li>Subsystems must be registered in the constructor.</li>\n <li>All non-trivial initializations must be made in the <code class=\"language-plaintext highlighter-rouge\">initialize()</code>` method.</li>\n <li>At the end of the <code class=\"language-plaintext highlighter-rouge\">main()</code> method, <code class=\"language-plaintext highlighter-rouge\">waitForTerminationRequest()</code> should be called.</li>\n</ul>\n\n<h4 id=\"6serverapplication-是-application-的子类\">6、<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的子类</h4>\n\n<p>Application 对其子类的要求是,如下这些成员函数必须被覆盖:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">initialize()</code> (the one-argument, protected variant):上一篇已介绍过。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">uninitialize()</code>:下面会介绍,Application 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数会在调用 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">reinitialize()</code></li>\n <li><code class=\"language-plaintext highlighter-rouge\">defineOptions()</code>:定义命令行启动选项。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">handleOption()</code>:响应相应的命令行选项。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">main()</code></li>\n</ul>\n\n<h4 id=\"7反初始化\">7、反初始化</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是继承 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的,<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是继承 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的。<code class=\"language-plaintext highlighter-rouge\">Application</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数会先调用 <code class=\"language-plaintext highlighter-rouge\">initialize()</code>,然后调用 <code class=\"language-plaintext highlighter-rouge\">main()</code>,最后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize</code>。最后这个反初始化过程,在 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 就是直接调用父类的 <code class=\"language-plaintext highlighter-rouge\">uninitialize</code> 函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">uninitialize</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">uninitialize</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"二cumulusserver-各项交互功能的源码解读\">二、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 各项交互功能的源码解读</h3>\n\n<h4 id=\"1命令行选项设定\">1、命令行选项设定</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的命令行选项有:<code class=\"language-plaintext highlighter-rouge\">log(l)</code>、<code class=\"language-plaintext highlighter-rouge\">dump(d)</code>、<code class=\"language-plaintext highlighter-rouge\">cirrus(c)</code>、<code class=\"language-plaintext highlighter-rouge\">middle(m)</code>、<code class=\"language-plaintext highlighter-rouge\">help(h)</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">defineOptions</span><span class=\"p\">(</span><span class=\"n\">OptionSet</span><span class=\"o\">&</span> <span class=\"n\">options</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">defineOptions</span><span class=\"p\">(</span><span class=\"n\">options</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>设定日志级别(0 - 8,默认是 6,表示 info 级别)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"log\"</span><span class=\"p\">,</span> <span class=\"s\">\"l\"</span><span class=\"p\">,</span> <span class=\"s\">\"Log level argument, must be beetween 0 and 8 : \\\n nothing, fatal, critic, error, warn, note, info, debug, trace. \\\n Default value is 6 (info), all logs until info level are displayed.\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">required</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">argument</span><span class=\"p\">(</span><span class=\"s\">\"level\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>其他一些选项:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"dump\"</span><span class=\"p\">,</span> <span class=\"s\">\"d\"</span><span class=\"p\">,</span> <span class=\"s\">\"Enables packet traces in logs. Optional arguments \\\n are 'middle' or 'all' respectively to displays just middle packet \\\n process or all packet process. If no argument is given, just outside \\\n packet process will be dumped.\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">,</span><span class=\"s\">\"middle|all\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"cirrus\"</span><span class=\"p\">,</span> <span class=\"s\">\"c\"</span><span class=\"p\">,</span> <span class=\"s\">\"Cirrus address to activate a 'man-in-the-middle' \\\n developer mode in bypassing flash packets to the official cirrus \\\n server of your choice, it's a instable mode to help Cumulus developers, \\\n </span><span class=\"se\">\\\"</span><span class=\"s\">p2p.rtmfp.net:10000</span><span class=\"se\">\\\"</span><span class=\"s\"> for example. By adding the 'dump' argument, \\\n you will able to display Cirrus/Flash packet exchange in your logs \\\n (see 'dump' argument).\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">,</span><span class=\"s\">\"address\"</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"middle\"</span><span class=\"p\">,</span> <span class=\"s\">\"m\"</span><span class=\"p\">,</span><span class=\"s\">\"Enables a 'man-in-the-middle' developer mode \\\n between two peers. It's a instable mode to help Cumulus developers. \\\n By adding the 'dump' argument, you will able to display Flash/Flash \\\n packet exchange in your logs (see 'dump' argument).\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>显示帮助信息的选项:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"help\"</span><span class=\"p\">,</span> <span class=\"s\">\"h\"</span><span class=\"p\">,</span> <span class=\"s\">\"Displays help information about command-line usage.\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">required</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">OptionSet</code> 是 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::OptionSet</code>,调用addOption可以向其中增加选项Option。其中required和repeatable表示:</p>\n\n<p>Sets whether the option is required (flag == true) or optional (flag == false).\nReturns true if the option can be specified more than once, or false if at most once.\n当需要显示帮助信息时,调用如下函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">displayHelp</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">HelpFormatter</span> <span class=\"n\">helpFormatter</span><span class=\"p\">(</span><span class=\"n\">options</span><span class=\"p\">());</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setCommand</span><span class=\"p\">(</span><span class=\"n\">commandName</span><span class=\"p\">());</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setUsage</span><span class=\"p\">(</span><span class=\"s\">\"OPTIONS\"</span><span class=\"p\">);</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setHeader</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer, open source RTMFP server\"</span><span class=\"p\">);</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">cout</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">setCommand()</code>: Sets the command name.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">setUsage()</code>: Sets the usage string.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">setHeader()</code>: Sets the header string.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">format()</code>: Writes the formatted help text to the given stream.</li>\n</ul>\n\n<h4 id=\"2处理命令行选项\">2、处理命令行选项</h4>\n\n<p>参数是选项名和选项值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">handleOption</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">handleOption</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果选项是帮助:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"help\"</span><span class=\"p\">)</span>\n <span class=\"n\">_helpRequested</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"cirrus\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">URI</span> <span class=\"n\">uri</span><span class=\"p\">(</span><span class=\"s\">\"rtmfp://\"</span><span class=\"o\">+</span><span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">SocketAddress</span><span class=\"p\">(</span><span class=\"n\">uri</span><span class=\"p\">.</span><span class=\"n\">getHost</span><span class=\"p\">(),</span><span class=\"n\">uri</span><span class=\"p\">.</span><span class=\"n\">getPort</span><span class=\"p\">());</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"Mode 'man in the middle' : the exchange will bypass to '%s'\"</span><span class=\"p\">,</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Mode 'man in the middle' error : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">message</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果选项是dump日志:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"dump\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">==</span> <span class=\"s\">\"all\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">ALL</span><span class=\"p\">);</span>\n <span class=\"k\">else</span> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">==</span> <span class=\"s\">\"middle\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">MIDDLE</span><span class=\"p\">);</span>\n <span class=\"k\">else</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">EXTERNAL</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果选项是middle:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"middle\"</span><span class=\"p\">)</span>\n <span class=\"n\">_middle</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果选项是log,表示设定日志级别:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"log\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetLevel</span><span class=\"p\">(</span><span class=\"n\">atoi</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">c_str</span><span class=\"p\">()));</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"6dump-logs\">6、Dump logs</h4>\n\n<p>先加一个作用域锁,然后再向日志流写数据。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">dumpHandler</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">data</span><span class=\"p\">,</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_logMutex</span><span class=\"p\">);</span>\n <span class=\"n\">cout</span><span class=\"p\">.</span><span class=\"n\">write</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">data</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">write</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">data</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">manageLogFile</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>调用 manageLogFile,主要做一些日志大小超出限制的处理。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">manageLogFile</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先判断是否超过日志文件的大小上线,LOG_SIZE是1000000字节(即约 1 MB)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">getSize</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"n\">LOG_SIZE</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"kt\">int</span> <span class=\"n\">num</span> <span class=\"o\">=</span> <span class=\"mi\">10</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>打开新日志文件:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span> <span class=\"nf\">file</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"s\">\"10\"</span><span class=\"p\">);</span>\n\n</code></pre></div></div>\n\n<p>如果该文件已经存在,则先删除:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">exists</span><span class=\"p\">())</span>\n <span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">remove</span><span class=\"p\">();</span>\n\n <span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">--</span><span class=\"n\">num</span> <span class=\"o\">>=</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">file</span> <span class=\"o\">=</span> <span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"n\">NumberFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">num</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">exists</span><span class=\"p\">())</span>\n <span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">renameTo</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"n\">NumberFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">num</span> <span class=\"o\">+</span> <span class=\"mi\">1</span><span class=\"p\">));</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">open</span><span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">path</span><span class=\"p\">(),</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">in</span> <span class=\"o\">|</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">ate</span><span class=\"p\">);</span>\n <span class=\"err\">}</span> \n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3停止运行\">3、停止运行</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 继承了 <code class=\"language-plaintext highlighter-rouge\">ApplicationKiller</code>,该类中有纯虚函数 <code class=\"language-plaintext highlighter-rouge\">kill()</code> 需要被实现,于是有:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">kill</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">terminate</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ApplicationKiller</code> 的定义在 <code class=\"language-plaintext highlighter-rouge\">ApplicationKiller.h</code> 中,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ApplicationKiller</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"p\">(){}</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">ApplicationKiller</span><span class=\"p\">(){}</span>\n \n <span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">kill</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"4载入配置\">4、载入配置</h4>\n\n<p>在initialize()函数中调用,上一篇已提到过。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">loadConfiguration</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">path</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">loadConfiguration</span><span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">);</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5处理日志\">5、处理日志</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">logHandler</span><span class=\"p\">(</span><span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">TID</span> <span class=\"n\">threadId</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">threadName</span><span class=\"p\">,</span>\n <span class=\"n\">Priority</span> <span class=\"n\">priority</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"kt\">char</span> <span class=\"o\">*</span><span class=\"n\">filePath</span><span class=\"p\">,</span>\n <span class=\"kt\">long</span> <span class=\"n\">line</span><span class=\"p\">,</span> \n <span class=\"k\">const</span> <span class=\"kt\">char</span> <span class=\"o\">*</span><span class=\"n\">text</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>作用域锁:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_logMutex</span><span class=\"p\">);</span>\n \n <span class=\"n\">Path</span> <span class=\"nf\">path</span><span class=\"p\">(</span><span class=\"n\">filePath</span><span class=\"p\">);</span>\n <span class=\"n\">string</span> <span class=\"n\">file</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getExtension</span><span class=\"p\">()</span> <span class=\"o\">==</span> <span class=\"s\">\"lua\"</span><span class=\"p\">)</span>\n <span class=\"n\">file</span> <span class=\"o\">+=</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">directory</span><span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">depth</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"s\">\"/\"</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是命令行交互模式(即不是 daemon 模式):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_isInteractive</span><span class=\"p\">)</span>\n <span class=\"n\">printf</span><span class=\"p\">(</span><span class=\"s\">\"%s %s[%ld] %s</span><span class=\"se\">\\n</span><span class=\"s\">\"</span><span class=\"p\">,</span>\n <span class=\"n\">g_logPriorities</span><span class=\"p\">[</span><span class=\"n\">priority</span> <span class=\"o\">-</span> <span class=\"mi\">1</span><span class=\"p\">],</span>\n <span class=\"p\">(</span><span class=\"n\">file</span> <span class=\"o\">+</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getBaseName</span><span class=\"p\">()).</span><span class=\"n\">c_str</span><span class=\"p\">(),</span>\n <span class=\"n\">line</span><span class=\"p\">,</span>\n <span class=\"n\">text</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>向日志流输出一句日志:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logStream</span> <span class=\"o\"><<</span> <span class=\"n\">DateTimeFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">LocalDateTime</span><span class=\"p\">(),</span><span class=\"s\">\"%d/%m %H:%M:%S.%c \"</span><span class=\"p\">)</span>\n <span class=\"o\"><<</span> <span class=\"n\">g_logPriorities</span><span class=\"p\">[</span><span class=\"n\">priority</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'\\t'</span> <span class=\"o\"><<</span> <span class=\"n\">threadName</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'('</span> <span class=\"o\"><<</span> <span class=\"n\">threadId</span> <span class=\"o\"><<</span> <span class=\"s\">\")</span><span class=\"se\">\\t</span><span class=\"s\">\"</span>\n <span class=\"o\"><<</span> <span class=\"p\">(</span><span class=\"n\">file</span> <span class=\"o\">+</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getFileName</span><span class=\"p\">())</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'['</span> <span class=\"o\"><<</span> <span class=\"n\">line</span> <span class=\"o\"><<</span> <span class=\"s\">\"] \"</span> \n <span class=\"o\"><<</span> <span class=\"n\">text</span> <span class=\"o\"><<</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">endl</span><span class=\"p\">;</span>\n \n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">flush</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>日志文件的善后处理(主要处理文件大小限制可能产生的问题):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">manageLogFile</span><span class=\"p\">();</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三maincpp-的-main-函数源码分析\">三、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数源码分析</h3>\n\n<h4 id=\"1maincpp-中的-main-函数中的-server\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中的 <code class=\"language-plaintext highlighter-rouge\">server</code></h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中真正启动的是 <code class=\"language-plaintext highlighter-rouge\">server</code>,它继承自 <code class=\"language-plaintext highlighter-rouge\">Cumulus::RTMFPServer</code>,而 <code class=\"language-plaintext highlighter-rouge\">Cumulus::RTMFPServer</code> 又继承自 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Startable</code>、<code class=\"language-plaintext highlighter-rouge\">Cumulus::Gateway</code>、<code class=\"language-plaintext highlighter-rouge\">Cumulus::Handler</code>。而 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Startable</code> 继承自 <code class=\"language-plaintext highlighter-rouge\">Poco::Runnable</code>,所以其是一个可以运行的线程。在 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusServer</code> 中,这是主线程。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span> <span class=\"nf\">server</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span> <span class=\"s\">\"./\"</span><span class=\"p\">),</span> <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span> <span class=\"n\">config</span><span class=\"p\">());</span>\n<span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer/Server.h</code> 中定义的,其构造函数的原型为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">root</span><span class=\"p\">,</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"o\">&</span> <span class=\"n\">applicationKiller</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">AbstractConfiguration</span><span class=\"o\">&</span> <span class=\"n\">configurations</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>个参数含义如下:</p>\n\n<blockquote>\n <p>The Path Root for the Server Application Killer for Termanting the Server Application Server Configuration</p>\n</blockquote>\n\n<p>距离来说,在我的 Worksapce 中:</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">root</code> 是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 构造函数的初始化列表极长:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span><span class=\"o\">::</span><span class=\"n\">Server</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">root</span><span class=\"p\">,</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"o\">&</span> <span class=\"n\">applicationKiller</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">AbstractConfiguration</span><span class=\"o\">&</span> <span class=\"n\">configurations</span><span class=\"p\">)</span> \n <span class=\"o\">:</span> <span class=\"n\">_blacklist</span><span class=\"p\">(</span><span class=\"n\">root</span> <span class=\"o\">+</span> <span class=\"s\">\"blacklist\"</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">),</span>\n <span class=\"n\">_applicationKiller</span><span class=\"p\">(</span><span class=\"n\">applicationKiller</span><span class=\"p\">),</span>\n <span class=\"n\">_hasOnRealTime</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_pService</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">luaMail</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"o\">=</span><span class=\"n\">Script</span><span class=\"o\">::</span><span class=\"n\">CreateState</span><span class=\"p\">(),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"smtp.host\"</span><span class=\"p\">,</span><span class=\"s\">\"localhost\"</span><span class=\"p\">),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"smtp.port\"</span><span class=\"p\">,</span><span class=\"n\">SMTPSession</span><span class=\"o\">::</span><span class=\"n\">SMTP_PORT</span><span class=\"p\">),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"smtp.timeout\"</span><span class=\"p\">,</span><span class=\"mi\">60</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>下面调用 <code class=\"language-plaintext highlighter-rouge\">Poco::File</code> 创建目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span><span class=\"p\">((</span><span class=\"n\">string</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">WWWPath</span> <span class=\"o\">=</span> <span class=\"n\">root</span> <span class=\"o\">+</span> <span class=\"s\">\"www\"</span><span class=\"p\">).</span><span class=\"n\">createDirectory</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>因为 <code class=\"language-plaintext highlighter-rouge\">roor</code> 是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 目录,所以 <code class=\"language-plaintext highlighter-rouge\">WWWPath</code> 就是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/www</code> 目录。然后初始化 <code class=\"language-plaintext highlighter-rouge\">GlobalTable</code>,这个 <code class=\"language-plaintext highlighter-rouge\">GlobalTable</code> 是和 Lua 有关的东东,这里暂不细说,先知道与 Lua 相关就好。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Service</span><span class=\"o\">::</span><span class=\"n\">InitGlobalTable</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>下面就涉及到了 Lua script 了:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SCRIPT_BEGIN</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">)</span>\n <span class=\"n\">SCRIPT_CREATE_PERSISTENT_OBJECT</span><span class=\"p\">(</span><span class=\"n\">Invoker</span><span class=\"p\">,</span><span class=\"n\">LUAInvoker</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span>\n <span class=\"n\">readNextConfig</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">,</span><span class=\"n\">configurations</span><span class=\"p\">,</span><span class=\"s\">\"\"</span><span class=\"p\">);</span>\n <span class=\"n\">lua_setglobal</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">,</span><span class=\"s\">\"cumulus.configs\"</span><span class=\"p\">);</span>\n <span class=\"n\">SCRIPT_END</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">SCRIPT_BEGIN</code>、<code class=\"language-plaintext highlighter-rouge\">SCRIPT_CREATE_PERSISTENT_OBJECT和SCRIPT_END</code> 都是宏,其定义在 <code class=\"language-plaintext highlighter-rouge\">Script.h</code> 文件中,如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#define SCRIPT_BEGIN(STATE) \\\n if (lua_State* __pState = STATE) { \\\n const char* __error=NULL;\n \n#define SCRIPT_CREATE_PERSISTENT_OBJECT(TYPE,LUATYPE,OBJ) \\\n Script::WritePersistentObject<TYPE,LUATYPE>(__pState,OBJ); \\\n lua_pop(__pState,1);\n \n#define SCRIPT_END }\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">SCRIPT_BEGIN和SCRIPT_END</code> 经常用到,当与 Lua 相关的操作出现时,都会以这两个宏作为开头和结尾。</p>\n\n<h4 id=\"2maincpp-中-main-函数的-serverstart\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的 <code class=\"language-plaintext highlighter-rouge\">server.start()</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">RTMFPServerParams</span><span class=\"o\">&</span> <span class=\"n\">params</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusServer</code> 正在运行,则返回并终止启动。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer server is yet running, call stop method before\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定端口号,如果端口号为 0,则返回并终止启动。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_port</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_port</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer port must have a positive value\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusEdge</code> 的端口号,如果其端口号与 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusSever</code> 端口号相同,则返回并终止启动:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_edgesPort</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesPort</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_port</span> <span class=\"o\">==</span> <span class=\"n\">_edgesPort</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer port must different than RTMFPServer edges.port\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Cirrus:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_freqManage</span> <span class=\"o\">=</span> <span class=\"mi\">2000000</span><span class=\"p\">;</span> <span class=\"c1\">// 2 sec by default</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">Target</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span><span class=\"p\">);</span>\n <span class=\"n\">_freqManage</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span> <span class=\"c1\">// no waiting, direct process in the middle case!</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer started in man-in-the-middle mode with server %s \\\n (unstable debug mode)\"</span><span class=\"p\">,</span> <span class=\"n\">_pCirrus</span><span class=\"o\">-></span><span class=\"n\">address</span><span class=\"p\">.</span><span class=\"n\">toString</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>middle:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_middle</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">middle</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_middle</span><span class=\"p\">)</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer started in man-in-the-middle mode between peers \\\n (unstable debug mode)\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>UDP Buffer:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"o\">==</span><span class=\"mi\">0</span> <span class=\"o\">?</span> \n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">getReceiveBufferSize</span><span class=\"p\">()</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"p\">;</span>\n \n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">setReceiveBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">setSendBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">setReceiveBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">setSendBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n \n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Socket buffer receving/sending size = %u/%u\"</span><span class=\"p\">,</span>\n <span class=\"n\">udpBufferSize</span><span class=\"p\">,</span>\n <span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n \n <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\"><</span> <span class=\"mi\">5</span> <span class=\"o\">?</span> <span class=\"mi\">5000</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\"><</span> <span class=\"mi\">5</span> <span class=\"o\">?</span> <span class=\"mi\">5000</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"n\">UInt8</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">edgesAttemptsBeforeFallback</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span><span class=\"p\">;</span>\n \n <span class=\"n\">setPriority</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">threadPriority</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>启动线程,进入循环运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">();</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>上句具体的源码实现为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果在运行则返回并终止启动。然后加一个局部锁。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果不得不join()到主线程中,那就join()吧</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>然后就运行这个线程吧:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_terminate</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3回顾一下整个启动流程\">3、回顾一下整个启动流程</h4>\n\n<p>到此我们先回顾一下启动过程:</p>\n\n<p>从 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的启动入口 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数开始,创建 <code class=\"language-plaintext highlighter-rouge\">Server</code> 对象并启动(调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 函数)。<code class=\"language-plaintext highlighter-rouge\">Server::start()</code> 中调用其父类(<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code>)的父类(<code class=\"language-plaintext highlighter-rouge\">Startable</code>)的方法 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code> 开启线程。\n调用 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code> 函数后,开启线城时传入的参数为 <code class=\"language-plaintext highlighter-rouge\">*this</code>,所以就会运行 <code class=\"language-plaintext highlighter-rouge\">Startable::run()</code>;</p>\n\n<h4 id=\"4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>、<code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(...)</code> 函数源码</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Startable::run()</code> 调用 <code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 函数,但这个函数被 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖,所以会运行 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>,其源码如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP server starts on %u port\"</span><span class=\"p\">,</span><span class=\"n\">_port</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果CumulusEdge:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP edges server starts on %u port\"</span><span class=\"p\">,</span><span class=\"n\">_edgesPort</span><span class=\"p\">);</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">onStart</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>运行线程:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>处理异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">what</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer unknown error\"</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果跳出了,则终止运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">onStop</span><span class=\"p\">();</span>\n \n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP server stops\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>该函数内部又会调用父类的 <code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 函数,该函数调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>它是一个纯虚函数,由 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 实现。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 会调用 <code class=\"language-plaintext highlighter-rouge\">void run(const volatile bool& terminate)</code> 方法,该方法被 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">run</span><span class=\"p\">(</span><span class=\"n\">_terminate</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"o\">!</span><span class=\"n\">_terminate</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的 <code class=\"language-plaintext highlighter-rouge\">run(const volatile bool &terminate)</code> 方法。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">...</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</title>\n \t<meta name=\"description\" content=\"RTMFP 是 Adobe 开发的基于 UDP 协议的实时传输媒体流协议,支持 P2P 传输,具有较高的实时性和安全性。它的主要应用场景是视频通信、语音通信和网络游戏。OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。Cumulus 是一个基于 OpenRTMFP 的服务器,提供 RTMFP 服务。它具有轻量级、跨平台和可扩展的特点,并且还提供了负载均衡和可扩展性解决方案。YY 语音的 Web 端音视频流媒体能力,正是基于 RTMFP 协议做的迭代优化实现的。本文是船长关于这个系列文章的第一篇。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</h2>\t\t\n\t<time datetime=\"2012-04-09T18:57:19+00:00\" class=\"by-line\">09 Apr 2012, 广州 | 作者 麦克船长 | 总计 8401 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一rtmfp是什么\" id=\"markdown-toc-一rtmfp是什么\">一、RTMFP 是什么?</a> <ul>\n <li><a href=\"#文件分享-p2p-和实时流媒体-p2p-的区别是什么\" id=\"markdown-toc-文件分享-p2p-和实时流媒体-p2p-的区别是什么\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</a></li>\n <li><a href=\"#rtmfp-和-rtmp-之间的区别是什么\" id=\"markdown-toc-rtmfp-和-rtmp-之间的区别是什么\">RTMFP 和 RTMP 之间的区别是什么?</a></li>\n <li><a href=\"#flash-player-支持-rtmfp-吗\" id=\"markdown-toc-flash-player-支持-rtmfp-吗\">Flash Player 支持 RTMFP 吗?</a></li>\n <li><a href=\"#cumulus-使用-adobe-的-cirrus-key-吗\" id=\"markdown-toc-cumulus-使用-adobe-的-cirrus-key-吗\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</a></li>\n <li><a href=\"#这个开源项目合法吗\" id=\"markdown-toc-这个开源项目合法吗\">这个开源项目合法吗?</a></li>\n </ul>\n </li>\n <li><a href=\"#二openrtmfp和cumulus\" id=\"markdown-toc-二openrtmfp和cumulus\">二、OpenRTMFP 和 Cumulus</a></li>\n <li><a href=\"#三入门介绍与部署cumulusserver\" id=\"markdown-toc-三入门介绍与部署cumulusserver\">三、入门介绍与部署 CumulusServer</a> <ul>\n <li><a href=\"#1背景介绍\" id=\"markdown-toc-1背景介绍\">1、背景介绍</a></li>\n <li><a href=\"#2准备工作\" id=\"markdown-toc-2准备工作\">2、准备工作</a></li>\n <li><a href=\"#3安装\" id=\"markdown-toc-3安装\">3、安装</a> <ul>\n <li><a href=\"#31外部依赖的安装\" id=\"markdown-toc-31外部依赖的安装\">3.1、外部依赖的安装</a></li>\n <li><a href=\"#32安装openrtmfpcumulus\" id=\"markdown-toc-32安装openrtmfpcumulus\">3.2、安装 OpenRTMFP/Cumulus</a></li>\n </ul>\n </li>\n <li><a href=\"#4配置\" id=\"markdown-toc-4配置\">4、配置</a></li>\n <li><a href=\"#5启动\" id=\"markdown-toc-5启动\">5、启动</a></li>\n <li><a href=\"#6基本使用\" id=\"markdown-toc-6基本使用\">6、基本使用</a></li>\n <li><a href=\"#7扩展cumulusserverserverapplication\" id=\"markdown-toc-7扩展cumulusserverserverapplication\">7、扩展 CumulusServer(Server Application)</a></li>\n </ul>\n </li>\n <li><a href=\"#三用lua编写helloworld应用扩展cumulusserver\" id=\"markdown-toc-三用lua编写helloworld应用扩展cumulusserver\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</a> <ul>\n <li><a href=\"#1server-side\" id=\"markdown-toc-1server-side\">1、Server-side</a> <ul>\n <li><a href=\"#11serverconfiguration\" id=\"markdown-toc-11serverconfiguration\">1.1、Server configuration</a></li>\n <li><a href=\"#12applicationfile\" id=\"markdown-toc-12applicationfile\">1.2、Application file</a></li>\n </ul>\n </li>\n <li><a href=\"#2client-side\" id=\"markdown-toc-2client-side\">2、Client-side</a></li>\n <li><a href=\"#3运行结果\" id=\"markdown-toc-3运行结果\">3、运行结果</a></li>\n <li><a href=\"#4远程测试一个免费的测试服务器\" id=\"markdown-toc-4远程测试一个免费的测试服务器\">4、远程测试:一个免费的测试服务器</a></li>\n </ul>\n </li>\n</ul>\n\n<h3 id=\"一rtmfp是什么\">一、RTMFP 是什么?</h3>\n\n<p>Real-Time Media Flow Protocol(RTMFP)是 Adobe 开发的一种基于 UDP 并支持 P2P 的实时传输媒体流。主要特点是:高传输效率(可以使用压缩和算法来优化流量从而提高传输效率)、高实时性(可以保证媒体流的实时性使得视频通信和其他实时通信更加流畅)、支持 P2P 传输(减少对服务器的依赖从而减少带宽和服务器资源消耗)、高安全性(加密媒体流从而保证其安全性)。</p>\n\n<p>RTMFP 的主要应用场景包括:视频通信(视频聊天和视频会议)、语音通信(语音聊天、电话)、网络游戏。不过 RTMFP 目前仅有 Adobe 开发的版本,所以它并不是个开源项目,而是个商业化服务。那么有没有开源版本呢?</p>\n\n<h4 id=\"文件分享-p2p-和实时流媒体-p2p-的区别是什么\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</h4>\n\n<p>RTMFP 是一个 P2P 系统,但它仅针对实时通信时直接用户到用户之间的通信而设计,不能用于多个对等方之间进行文件共享(使用分段下载)。Facebook 在其 Pipe 应用中使用此协议将大文件直接在两个用户之间传输。</p>\n\n<h4 id=\"rtmfp-和-rtmp-之间的区别是什么\">RTMFP 和 RTMP 之间的区别是什么?</h4>\n\n<p>RTMP 是实时消息协议,RTMFP 代表实时媒体流协议。RTMFP 基于用户数据报协议(UDP),而 RTMP 基于传输控制协议(TCP)。\n与 RTMP 不同,RTMFP 还支持直接从一个 Adobe Flash Player 传输数据到另一个,而无需经过服务器。</p>\n\n<h4 id=\"flash-player-支持-rtmfp-吗\">Flash Player 支持 RTMFP 吗?</h4>\n\n<p>RTMFP 是基于用户数据报协议(UDP)的,而 RTMP 是基于传输控制协议(TCP)的。与 RTMP 不同,RTMFP 还支持直接在两个 Adobe Flash Player 之间发送数据,而不经过服务器。Flash Player 10.0 仅允许一对一通信进行 P2P,但从 10.1 开始允许应用程序级别的多播。Flash Player 查找适当的分发路由(覆盖网络),并可以将其分发到通过 P2P 连接的组。</p>\n\n<h4 id=\"cumulus-使用-adobe-的-cirrus-key-吗\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</h4>\n\n<p>不!当然,这是Cumulus的主要目标:成为Cirrus GPL的替代品。唯一的限制是:你的CPU,内存和单台机器的端口数。</p>\n\n<h4 id=\"这个开源项目合法吗\">这个开源项目合法吗?</h4>\n\n<p>在美国,数字千年版权法(Digital Millennium Copyright Act)规定,逆向工程用于协议互操作性是合法的。你可以在 WikiPedia 上查看相关讨论:</p>\n\n<ul>\n <li>http://en.wikipedia.org/wiki/Real_Time_Media_Flow_Protocol</li>\n <li>http://en.wikipedia.org/wiki/Proprietary_protocol</li>\n <li>http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act</li>\n</ul>\n\n<p>当逆向工程的目的是协议互操作性时,有法律先例。在美国,数字千年版权法(Digital Millennium Copyright Act)为逆向工程软件以使其与其他软件互操作提供了安全保障。</p>\n\n<h3 id=\"二openrtmfp和cumulus\">二、OpenRTMFP 和 Cumulus</h3>\n\n<p>OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。它包含了 RTMFP 协议的实现,以及一些额外的功能,如媒体流传输、P2P 通信、脚本引擎和数据存储。</p>\n\n<p>Cumulus 是一个基于 OpenRTMFP 的服务器,是一个完整的开源且跨平台的 RTMFP 服务器,可通过脚本进行扩展。CumulusServer 根据 GPL 许可在考虑以下 4 个概念的情况下开发:速度、轻量、跨平台和可扩展。尽管尚未发布版本,但只有在 CumulusServer 经过测试和批准后才会将代码推送到 github。实际上,主要稳定的功能有:</p>\n\n<ul>\n <li>P2P rendez-vous service</li>\n <li>live streaming</li>\n <li>RPC、pull、push exchange,实际上客户端和服务器之间的所有 AMF 可能交换</li>\n <li>脚本引擎,用于创建自己的应用服务器或扩展 Cumulus 的功能</li>\n <li>可扩展性和负载均衡解决方案</li>\n</ul>\n\n<p>下面的内容是本篇 blog 的重点,包括两部分:先是 OpenRTMFP 应用的核心 CumulusServer 的入门介绍与部署,然后用 Lua 编写 HelloWorld 应用扩展 CumulusServer,我们开始吧!</p>\n\n<h3 id=\"三入门介绍与部署cumulusserver\">三、入门介绍与部署 CumulusServer</h3>\n\n<h4 id=\"1背景介绍\">1、背景介绍</h4>\n\n<p>OpenRTMFP 可以帮助你实现 Flash 的实时应用的高并发扩展,OpenRTMFP/Cumulus 是基于 GNU General Public License 的。</p>\n\n<p>POCO:POrtable COmponents,是一个强大的开源 C++ 库。其在 C++ 开发中的角色,相当于 Java Class Library、苹果的 Cocoa、.NET framework。</p>\n\n<h4 id=\"2准备工作\">2、准备工作</h4>\n\n<p>下载:</p>\n\n<table>\n <thead>\n <tr>\n <th><strong>External Dependencies</strong></th>\n <th><strong>Official Site</strong></th>\n <th><strong>Windows</strong></th>\n <th><strong>Linux/OSX</strong></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>OpenSSL</td>\n <td><a href=\"http://www.slproweb.com/products/Win32OpenSSL.html\">Official Site</a></td>\n <td><a href=\"http://www.slproweb.com/download/Win32OpenSSL_Light-1_0_1.exe\">Download</a></td>\n <td><a href=\"http://www.openssl.org/source/openssl-1.0.1.tar.gz\">Download</a></td>\n </tr>\n <tr>\n <td>Lua</td>\n <td><a href=\"http://www.lua.org/\">Official Site</a></td>\n <td><a href=\"http://luaforwindows.googlecode.com/files/LuaForWindows_v5.1.4-45.exe\">Download</a></td>\n <td><a href=\"http://www.lua.org/ftp/lua-5.1.5.tar.gz\">Download</a></td>\n </tr>\n <tr>\n <td>POCO</td>\n <td><a href=\"http://pocoproject.org/\">Official Site</a></td>\n <td><a href=\"http://downloads.sourceforge.net/project/poco/sources/poco-1.4.3/poco-1.4.3p1.zip\">Download</a></td>\n <td><a href=\"https://sourceforge.net/projects/poco/files/sources/poco-1.4.3/poco-1.4.3p1.tar.gz/download\">Download</a></td>\n </tr>\n </tbody>\n</table>\n\n<p>注意:POCO for linux 版本必须是 1.4.0 或更高,否则会引起 TCP 相关的 bug。</p>\n\n<h4 id=\"3安装\">3、安装</h4>\n\n<h5 id=\"31外部依赖的安装\">3.1、外部依赖的安装</h5>\n\n<p>Windows 下略,Linux 下基本就是:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>./configuremakesudo\n<span class=\"nv\">$ </span>make <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<h5 id=\"32安装openrtmfpcumulus\">3.2、安装 OpenRTMFP/Cumulus</h5>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">cd </span>OpenRTMFP-Cumulus/CumulusLib\n<span class=\"nv\">$ </span>make\n<span class=\"nv\">$ </span><span class=\"nb\">cd</span> ../CumulusServer\n<span class=\"nv\">$ </span>make\n</code></pre></div></div>\n\n<p>如果出现了 <code class=\"language-plaintext highlighter-rouge\">.h</code> 文件、lib 库找不到的情况,请修改 OpenRTMFP-Cumulus/CumulusLib/Makefile 或 OpenRTMFP-Cumulus/CumulusServer/Makefile。</p>\n\n<h4 id=\"4配置\">4、配置</h4>\n\n<p>通过编写 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP-Cumulus/CumulusServer/CumulusServer.ini</code> 文件来为 OpenRTMFP-Cumulus 进行个性化配置(默认是没有这个文件的),这个文件的内容形如:</p>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">;</span><span class=\"n\">CumulusServer</span><span class=\"p\">.</span><span class=\"n\">ini</span>\n<span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"mi\">1985</span>\n<span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"mi\">114688</span>\n<span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n<span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"mi\">15</span>\n<span class=\"p\">[</span><span class=\"n\">logs</span><span class=\"p\">]</span>\n<span class=\"n\">name</span><span class=\"o\">=</span><span class=\"n\">log</span>\n<span class=\"n\">directory</span><span class=\"o\">=</span><span class=\"n\">C</span><span class=\"p\">:</span><span class=\"o\">/</span><span class=\"n\">CumulusServer</span><span class=\"o\">/</span><span class=\"n\">logs</span>\n</code></pre></div></div>\n\n<p>一些字段的设置含义如下,摘自:<a href=\"https://github.com/OpenRTMFP/Cumulus/wiki/Installation\">地址</a>。</p>\n\n<ul>\n <li>公开给 Client 的端口号 <code class=\"language-plaintext highlighter-rouge\">port</code>,默认值是 1935(RTMFP 服务器的默认端口),用于 CumulusServer 监听 RTMFP 请求。</li>\n <li>UDP 缓冲区字节数 <code class=\"language-plaintext highlighter-rouge\">udpBufferSize</code>, allows to change the size in bytes of UDP reception and sending buffer. Increases this value if your operating system has a default value too lower for important loads.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">keepAliveServer</code>, time in seconds for periodically sending packets keep-alive with server, 15s by default (valid value is from 5s to 255s).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">keepAlivePeer</code>, time in seconds for periodically sending packets keep-alive between peers, 10s by default (valid value is from 5s to 255s).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.activated</code>, activate or not the edges server on the RTMFP server (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, CumulusServer stays a RTMFP server without edges ability (default value is false).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.port</code>, port for the edges server, to accept incoming new CumulusEdge instances (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, it’s the port 1936.</li>\n</ul>\n\n<blockquote>\n <p>Warning: This port will receive plain text request from edges, for this purpose it should not be made public. It’s very important for security consideration. It must be available only for CumulusEdge instances, and anything else.</p>\n</blockquote>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.attemptsBeforeFallback</code>, number of CumulusEdge attempt connections before falling back to CumulusServer (see CumulusEdge, Scalability page for more details about CumulusEdge). By default the value is 2 (in practical, 2 attempts happens after 5 sec approximately).</li>\n <li>SMTP IP 地址 <code class=\"language-plaintext highlighter-rouge\">smtp.host</code>, configure a SMTP host to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is localhost.</li>\n <li>SMTP 端口 <code class=\"language-plaintext highlighter-rouge\">smtp.port</code>, configure a SMTP port to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 25.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">smtp.timeout</code>, configure a SMTP timeout session in seconds to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 60 seconds.</li>\n <li>日志路径 <code class=\"language-plaintext highlighter-rouge\">logs.directory</code>,默认是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer/logsby</code>。</li>\n <li>日志文件名称 <code class=\"language-plaintext highlighter-rouge\">logs.name</code>,默认是<code class=\"language-plaintext highlighter-rouge\">log</code>。</li>\n</ul>\n\n<h4 id=\"5启动\">5、启动</h4>\n\n<p>Windows 下的启动方法为:</p>\n\n<pre><code class=\"language-dos\">$ CumulusServer.exe /registerService [/displayName=CumulusServer /description=\"Open Source RTMFP Server\" /startup=automatic]\n</code></pre>\n\n<p>Unix-like 下的启动方法为:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">sudo</span> ./CumulusServer <span class=\"nt\">--daemon</span> <span class=\"o\">[</span><span class=\"nt\">--pidfile</span><span class=\"o\">=</span>/var/run/CumulusServer.pid]\n</code></pre></div></div>\n\n<p>具体地,我的启动命令为:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">sudo</span> ./CumulusServer <span class=\"nt\">--daemon</span> <span class=\"nt\">--pidfile</span><span class=\"o\">=</span>./CumulusServer.pid\n</code></pre></div></div>\n\n<h4 id=\"6基本使用\">6、基本使用</h4>\n\n<p>本地 Flash client 可以通过如下语句连接:</p>\n\n<div class=\"language-as highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">$</span> <span class=\"kd\">var</span> <span class=\"nx\">nc</span><span class=\"o\">:</span><span class=\"nx\">NetConnection</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nx\">NetConnection</span><span class=\"p\">()</span><span class=\"o\">;</span><span class=\"nx\">nc</span><span class=\"p\">.</span><span class=\"nx\">connect</span><span class=\"p\">(</span><span class=\"s2\">\"rtmfp://localhost/\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>RTMFP默认是采用1935端口,如果你特别指定了其他端口,比如12345,请使用如下方式:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>nc.connect(\"rtmfp://localhost:12345/\");\n</code></pre></div></div>\n\n<h4 id=\"7扩展cumulusserverserverapplication\">7、扩展 CumulusServer(Server Application)</h4>\n\n<p>启动CumulusServer后,会在可执行文件的目录下出现一个www目录,该目录的作用,就是作为 Server Application 的默认根目录。具体的对应关系如下:</p>\n\n<blockquote>\n <p>rtmfp://host:port/ -> [CumulusServer folder]/www/main.lua (root application)</p>\n</blockquote>\n\n<blockquote>\n <p>rtmfp://host:port/myApplication -> [CumulusServer folder]/www/myApplication/main.lua</p>\n</blockquote>\n\n<blockquote>\n <p>rtmfp://host:port/Games/myGame -> [CumulusServer folder]/www/Games/myGame/main.lua</p>\n</blockquote>\n\n<p>另外要提醒的是,如果main.lua文件被修改,则不需要重启 CumulusServer,因为 Server Application 的创建是一种动态的方式。</p>\n\n<h3 id=\"三用lua编写helloworld应用扩展cumulusserver\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</h3>\n\n<p>下面的这个实例是在本地(Client 与 Server 位于同一机器上)测试的。</p>\n\n<h4 id=\"1server-side\">1、Server-side</h4>\n\n<h5 id=\"11serverconfiguration\">1.1、Server configuration</h5>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">;</span> <span class=\"n\">CumulusServer</span><span class=\"p\">.</span><span class=\"n\">ini</span>\n<span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"mi\">1935</span>\n<span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"mi\">114688</span>\n<span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n<span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"mi\">15</span>\n<span class=\"p\">[</span><span class=\"n\">logs</span><span class=\"p\">]</span><span class=\"n\">name</span> <span class=\"o\">=</span> <span class=\"n\">log</span>\n<span class=\"n\">directory</span> <span class=\"o\">=</span> <span class=\"n\">logs</span>\n</code></pre></div></div>\n\n<h5 id=\"12applicationfile\">1.2、Application file</h5>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">function</span> <span class=\"nf\">onConnection</span><span class=\"p\">(</span><span class=\"n\">client</span><span class=\"p\">,</span><span class=\"n\">response</span><span class=\"p\">,</span><span class=\"o\">...</span><span class=\"p\">)</span>\n <span class=\"k\">function</span> <span class=\"nf\">client</span><span class=\"p\">:</span><span class=\"n\">test</span><span class=\"p\">(</span><span class=\"o\">...</span><span class=\"p\">)</span>\n <span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">firstname</span> <span class=\"o\">=</span> <span class=\"n\">unpack</span><span class=\"p\">(</span><span class=\"n\">arg</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"s2\">\"Hello \"</span><span class=\"o\">..</span><span class=\"n\">firstname</span><span class=\"o\">..</span><span class=\"s2\">\" \"</span><span class=\"o\">..</span><span class=\"n\">name</span>\n <span class=\"k\">end</span>\n <span class=\"k\">end</span>\n</code></pre></div></div>\n\n<h4 id=\"2client-side\">2、Client-side</h4>\n\n<div class=\"language-java highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// CumulusClient.as</span>\n\n<span class=\"kn\">package</span> <span class=\"err\">{</span>\n <span class=\"nn\">import</span> <span class=\"n\">flash</span><span class=\"o\">.</span><span class=\"na\">display</span><span class=\"o\">.</span><span class=\"na\">Sprite</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.NetConnection</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.NetStream</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.Responder</span><span class=\"o\">;</span>\n\n <span class=\"kd\">public</span> <span class=\"kd\">class</span> <span class=\"nc\">CumulusClient</span> <span class=\"kd\">extends</span> <span class=\"nc\">Sprite</span> <span class=\"o\">{</span>\n <span class=\"kd\">private</span> <span class=\"kt\">var</span> <span class=\"nl\">nc:</span><span class=\"nc\">NetConnection</span> <span class=\"o\">=</span> <span class=\"kc\">null</span><span class=\"o\">;</span>\n \t<span class=\"kd\">private</span> <span class=\"kt\">var</span> <span class=\"nl\">ns:</span><span class=\"nc\">NetStream</span> <span class=\"o\">=</span> <span class=\"kc\">null</span><span class=\"o\">;</span>\n \t\n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">CumulusClient</span><span class=\"o\">()</span> <span class=\"o\">{</span>\n <span class=\"n\">nc</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nc\">NetConnection</span><span class=\"o\">();</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">connect</span><span class=\"o\">(</span><span class=\"s\">\"rtmfp://localhost\"</span><span class=\"o\">);</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">client</span> <span class=\"o\">=</span> <span class=\"k\">this</span><span class=\"o\">;</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">call</span><span class=\"o\">(</span><span class=\"s\">\"test\"</span><span class=\"o\">,</span><span class=\"k\">new</span> <span class=\"nc\">Responder</span><span class=\"o\">(</span><span class=\"n\">onResult</span><span class=\"o\">,</span><span class=\"n\">onStatus</span><span class=\"o\">),</span> <span class=\"s\">\"OpenRTMFP/Cumulus\"</span><span class=\"o\">,</span> <span class=\"s\">\"World\"</span><span class=\"o\">)</span>\n <span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">close</span><span class=\"o\">():</span><span class=\"kt\">void</span> <span class=\"o\">{</span> \n\t\t\t<span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">close</span><span class=\"o\">();</span>\n \t<span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">onStatus</span><span class=\"o\">(</span><span class=\"nl\">status:</span><span class=\"nc\">Object</span><span class=\"o\">):</span><span class=\"kt\">void</span> <span class=\"o\">{</span>\n \t<span class=\"n\">trace</span><span class=\"o\">(</span><span class=\"n\">status</span><span class=\"o\">.</span><span class=\"na\">description</span><span class=\"o\">)</span>\n\t <span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">onResult</span><span class=\"o\">(</span><span class=\"nl\">response:</span><span class=\"nc\">Object</span><span class=\"o\">):</span><span class=\"kt\">void</span> <span class=\"o\">{</span>\n \t<span class=\"n\">trace</span><span class=\"o\">(</span><span class=\"n\">response</span><span class=\"o\">)</span> <span class=\"c1\">// expected to display \"Hello World OpenRTMFP/Cumulus\" </span>\n\t <span class=\"o\">}</span> \n\t<span class=\"o\">}</span>\n<span class=\"o\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3运行结果\">3、运行结果</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Hello World OpenRTMFP/Cumulus\n[SWF] CumulusClient.swf - 解压缩后为 1,776 个字节\n[卸装 SWF] CumulusClient.swf\n</code></pre></div></div>\n\n<h4 id=\"4远程测试一个免费的测试服务器\">4、远程测试:一个免费的测试服务器</h4>\n\n<p>获取 Developer Key 的地址:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>http://108.59.252.39:8080/CumulusServer/index.jsp\n</code></pre></div></div>\n\n<p>服务器配置信息:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Server: amd64 OS: Linux 2.6.18-028stab095.1\nServer IP: 108.59.252.39\nOpenRTMFP as of: 22.Feb.2012\n</code></pre></div></div>\n\n<p>编写服务器段应用地址:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>http://108.59.252.39:8080/CumulusServer/manage_ssls.jsp\n</code></pre></div></div>\n\n<p>快去试试吧 :)</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"static_files":[{"basename":"KaTeX_AMS-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_AMS-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.ttf"},{"basename":"KaTeX_AMS-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_AMS-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.woff"},{"basename":"KaTeX_AMS-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_AMS-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.woff2"},{"basename":"KaTeX_Caligraphic-Bold","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.ttf"},{"basename":"KaTeX_Caligraphic-Bold","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.woff"},{"basename":"KaTeX_Caligraphic-Bold","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.woff2"},{"basename":"KaTeX_Caligraphic-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.ttf"},{"basename":"KaTeX_Caligraphic-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.woff"},{"basename":"KaTeX_Caligraphic-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.woff2"},{"basename":"KaTeX_Fraktur-Bold","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.ttf"},{"basename":"KaTeX_Fraktur-Bold","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.woff"},{"basename":"KaTeX_Fraktur-Bold","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.woff2"},{"basename":"KaTeX_Fraktur-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.ttf"},{"basename":"KaTeX_Fraktur-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.woff"},{"basename":"KaTeX_Fraktur-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.woff2"},{"basename":"KaTeX_Main-Bold","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.ttf"},{"basename":"KaTeX_Main-Bold","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.woff"},{"basename":"KaTeX_Main-Bold","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.woff2"},{"basename":"KaTeX_Main-BoldItalic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-BoldItalic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.ttf"},{"basename":"KaTeX_Main-BoldItalic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-BoldItalic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.woff"},{"basename":"KaTeX_Main-BoldItalic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-BoldItalic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.woff2"},{"basename":"KaTeX_Main-Italic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.ttf"},{"basename":"KaTeX_Main-Italic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.woff"},{"basename":"KaTeX_Main-Italic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.woff2"},{"basename":"KaTeX_Main-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.ttf"},{"basename":"KaTeX_Main-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.woff"},{"basename":"KaTeX_Main-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.woff2"},{"basename":"KaTeX_Math-BoldItalic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-BoldItalic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.ttf"},{"basename":"KaTeX_Math-BoldItalic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-BoldItalic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.woff"},{"basename":"KaTeX_Math-BoldItalic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-BoldItalic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.woff2"},{"basename":"KaTeX_Math-Italic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.ttf"},{"basename":"KaTeX_Math-Italic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.woff"},{"basename":"KaTeX_Math-Italic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.woff2"},{"basename":"KaTeX_SansSerif-Bold","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.ttf"},{"basename":"KaTeX_SansSerif-Bold","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.woff"},{"basename":"KaTeX_SansSerif-Bold","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.woff2"},{"basename":"KaTeX_SansSerif-Italic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.ttf"},{"basename":"KaTeX_SansSerif-Italic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.woff"},{"basename":"KaTeX_SansSerif-Italic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.woff2"},{"basename":"KaTeX_SansSerif-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.ttf"},{"basename":"KaTeX_SansSerif-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.woff"},{"basename":"KaTeX_SansSerif-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.woff2"},{"basename":"KaTeX_Script-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Script-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.ttf"},{"basename":"KaTeX_Script-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Script-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.woff"},{"basename":"KaTeX_Script-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Script-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.woff2"},{"basename":"KaTeX_Size1-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size1-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.ttf"},{"basename":"KaTeX_Size1-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size1-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.woff"},{"basename":"KaTeX_Size1-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size1-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.woff2"},{"basename":"KaTeX_Size2-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size2-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.ttf"},{"basename":"KaTeX_Size2-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size2-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.woff"},{"basename":"KaTeX_Size2-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size2-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.woff2"},{"basename":"KaTeX_Size3-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size3-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.ttf"},{"basename":"KaTeX_Size3-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size3-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.woff"},{"basename":"KaTeX_Size3-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size3-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.woff2"},{"basename":"KaTeX_Size4-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size4-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.ttf"},{"basename":"KaTeX_Size4-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size4-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.woff"},{"basename":"KaTeX_Size4-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size4-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.woff2"},{"basename":"KaTeX_Typewriter-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Typewriter-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.ttf"},{"basename":"KaTeX_Typewriter-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Typewriter-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.woff"},{"basename":"KaTeX_Typewriter-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Typewriter-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.woff2"},{"basename":"katex.min","extname":".css","path":"/assets/plugins/katex.0.11.1/katex.min.css","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"katex.min.css"},{"basename":"syntax","extname":".css","path":"/css/syntax.css","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"syntax.css"},{"basename":"KaTeX_AMS-Regular","extname":".ttf","path":"/fonts/KaTeX_AMS-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.ttf"},{"basename":"KaTeX_AMS-Regular","extname":".woff","path":"/fonts/KaTeX_AMS-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.woff"},{"basename":"KaTeX_AMS-Regular","extname":".woff2","path":"/fonts/KaTeX_AMS-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.woff2"},{"basename":"KaTeX_Caligraphic-Bold","extname":".ttf","path":"/fonts/KaTeX_Caligraphic-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.ttf"},{"basename":"KaTeX_Caligraphic-Bold","extname":".woff","path":"/fonts/KaTeX_Caligraphic-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.woff"},{"basename":"KaTeX_Caligraphic-Bold","extname":".woff2","path":"/fonts/KaTeX_Caligraphic-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.woff2"},{"basename":"KaTeX_Caligraphic-Regular","extname":".ttf","path":"/fonts/KaTeX_Caligraphic-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.ttf"},{"basename":"KaTeX_Caligraphic-Regular","extname":".woff","path":"/fonts/KaTeX_Caligraphic-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.woff"},{"basename":"KaTeX_Caligraphic-Regular","extname":".woff2","path":"/fonts/KaTeX_Caligraphic-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.woff2"},{"basename":"KaTeX_Fraktur-Bold","extname":".ttf","path":"/fonts/KaTeX_Fraktur-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.ttf"},{"basename":"KaTeX_Fraktur-Bold","extname":".woff","path":"/fonts/KaTeX_Fraktur-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.woff"},{"basename":"KaTeX_Fraktur-Bold","extname":".woff2","path":"/fonts/KaTeX_Fraktur-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.woff2"},{"basename":"KaTeX_Fraktur-Regular","extname":".ttf","path":"/fonts/KaTeX_Fraktur-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.ttf"},{"basename":"KaTeX_Fraktur-Regular","extname":".woff","path":"/fonts/KaTeX_Fraktur-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.woff"},{"basename":"KaTeX_Fraktur-Regular","extname":".woff2","path":"/fonts/KaTeX_Fraktur-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.woff2"},{"basename":"KaTeX_Main-Bold","extname":".ttf","path":"/fonts/KaTeX_Main-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.ttf"},{"basename":"KaTeX_Main-Bold","extname":".woff","path":"/fonts/KaTeX_Main-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.woff"},{"basename":"KaTeX_Main-Bold","extname":".woff2","path":"/fonts/KaTeX_Main-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.woff2"},{"basename":"KaTeX_Main-BoldItalic","extname":".ttf","path":"/fonts/KaTeX_Main-BoldItalic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.ttf"},{"basename":"KaTeX_Main-BoldItalic","extname":".woff","path":"/fonts/KaTeX_Main-BoldItalic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.woff"},{"basename":"KaTeX_Main-BoldItalic","extname":".woff2","path":"/fonts/KaTeX_Main-BoldItalic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.woff2"},{"basename":"KaTeX_Main-Italic","extname":".ttf","path":"/fonts/KaTeX_Main-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.ttf"},{"basename":"KaTeX_Main-Italic","extname":".woff","path":"/fonts/KaTeX_Main-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.woff"},{"basename":"KaTeX_Main-Italic","extname":".woff2","path":"/fonts/KaTeX_Main-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.woff2"},{"basename":"KaTeX_Main-Regular","extname":".ttf","path":"/fonts/KaTeX_Main-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.ttf"},{"basename":"KaTeX_Main-Regular","extname":".woff","path":"/fonts/KaTeX_Main-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.woff"},{"basename":"KaTeX_Main-Regular","extname":".woff2","path":"/fonts/KaTeX_Main-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.woff2"},{"basename":"KaTeX_Math-BoldItalic","extname":".ttf","path":"/fonts/KaTeX_Math-BoldItalic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.ttf"},{"basename":"KaTeX_Math-BoldItalic","extname":".woff","path":"/fonts/KaTeX_Math-BoldItalic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.woff"},{"basename":"KaTeX_Math-BoldItalic","extname":".woff2","path":"/fonts/KaTeX_Math-BoldItalic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.woff2"},{"basename":"KaTeX_Math-Italic","extname":".ttf","path":"/fonts/KaTeX_Math-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.ttf"},{"basename":"KaTeX_Math-Italic","extname":".woff","path":"/fonts/KaTeX_Math-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.woff"},{"basename":"KaTeX_Math-Italic","extname":".woff2","path":"/fonts/KaTeX_Math-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.woff2"},{"basename":"KaTeX_SansSerif-Bold","extname":".ttf","path":"/fonts/KaTeX_SansSerif-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.ttf"},{"basename":"KaTeX_SansSerif-Bold","extname":".woff","path":"/fonts/KaTeX_SansSerif-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.woff"},{"basename":"KaTeX_SansSerif-Bold","extname":".woff2","path":"/fonts/KaTeX_SansSerif-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.woff2"},{"basename":"KaTeX_SansSerif-Italic","extname":".ttf","path":"/fonts/KaTeX_SansSerif-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.ttf"},{"basename":"KaTeX_SansSerif-Italic","extname":".woff","path":"/fonts/KaTeX_SansSerif-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.woff"},{"basename":"KaTeX_SansSerif-Italic","extname":".woff2","path":"/fonts/KaTeX_SansSerif-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.woff2"},{"basename":"KaTeX_SansSerif-Regular","extname":".ttf","path":"/fonts/KaTeX_SansSerif-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.ttf"},{"basename":"KaTeX_SansSerif-Regular","extname":".woff","path":"/fonts/KaTeX_SansSerif-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.woff"},{"basename":"KaTeX_SansSerif-Regular","extname":".woff2","path":"/fonts/KaTeX_SansSerif-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.woff2"},{"basename":"KaTeX_Script-Regular","extname":".ttf","path":"/fonts/KaTeX_Script-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.ttf"},{"basename":"KaTeX_Script-Regular","extname":".woff","path":"/fonts/KaTeX_Script-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.woff"},{"basename":"KaTeX_Script-Regular","extname":".woff2","path":"/fonts/KaTeX_Script-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.woff2"},{"basename":"KaTeX_Size1-Regular","extname":".ttf","path":"/fonts/KaTeX_Size1-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.ttf"},{"basename":"KaTeX_Size1-Regular","extname":".woff","path":"/fonts/KaTeX_Size1-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.woff"},{"basename":"KaTeX_Size1-Regular","extname":".woff2","path":"/fonts/KaTeX_Size1-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.woff2"},{"basename":"KaTeX_Size2-Regular","extname":".ttf","path":"/fonts/KaTeX_Size2-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.ttf"},{"basename":"KaTeX_Size2-Regular","extname":".woff","path":"/fonts/KaTeX_Size2-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.woff"},{"basename":"KaTeX_Size2-Regular","extname":".woff2","path":"/fonts/KaTeX_Size2-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.woff2"},{"basename":"KaTeX_Size3-Regular","extname":".ttf","path":"/fonts/KaTeX_Size3-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.ttf"},{"basename":"KaTeX_Size3-Regular","extname":".woff","path":"/fonts/KaTeX_Size3-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.woff"},{"basename":"KaTeX_Size3-Regular","extname":".woff2","path":"/fonts/KaTeX_Size3-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.woff2"},{"basename":"KaTeX_Size4-Regular","extname":".ttf","path":"/fonts/KaTeX_Size4-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.ttf"},{"basename":"KaTeX_Size4-Regular","extname":".woff","path":"/fonts/KaTeX_Size4-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.woff"},{"basename":"KaTeX_Size4-Regular","extname":".woff2","path":"/fonts/KaTeX_Size4-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.woff2"},{"basename":"KaTeX_Typewriter-Regular","extname":".ttf","path":"/fonts/KaTeX_Typewriter-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.ttf"},{"basename":"KaTeX_Typewriter-Regular","extname":".woff","path":"/fonts/KaTeX_Typewriter-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.woff"},{"basename":"KaTeX_Typewriter-Regular","extname":".woff2","path":"/fonts/KaTeX_Typewriter-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.woff2"},{"basename":"avatar","extname":".jpg","path":"/img/about/avatar.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"avatar.jpg"},{"basename":"photo_1","extname":".jpg","path":"/img/about/photo_1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_1.jpg"},{"basename":"photo_10","extname":".jpg","path":"/img/about/photo_10.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_10.jpg"},{"basename":"photo_2","extname":".jpg","path":"/img/about/photo_2.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_2.jpg"},{"basename":"photo_3","extname":".jpg","path":"/img/about/photo_3.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_3.jpg"},{"basename":"photo_4","extname":".jpg","path":"/img/about/photo_4.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_4.jpg"},{"basename":"photo_5","extname":".jpg","path":"/img/about/photo_5.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_5.jpg"},{"basename":"photo_6","extname":".jpg","path":"/img/about/photo_6.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_6.jpg"},{"basename":"photo_7","extname":".JPG","path":"/img/about/photo_7.JPG","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_7.JPG"},{"basename":"photo_8","extname":".JPG","path":"/img/about/photo_8.JPG","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_8.JPG"},{"basename":"photo_9","extname":".jpg","path":"/img/about/photo_9.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_9.jpg"},{"basename":"favicon","extname":".png","path":"/img/favicon.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"favicon.png"},{"basename":"img-test","extname":".png","path":"/img/img-test.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"img-test.png"},{"basename":"monochrome-mobile","extname":".png","path":"/img/monochrome-mobile.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"monochrome-mobile.png"},{"basename":"monochrome","extname":".svg","path":"/img/monochrome.svg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"monochrome.svg"},{"basename":"monochrome01","extname":".png","path":"/img/monochrome01.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"monochrome01.png"},{"basename":"2012-04-24-openrtmfp-cumulus-4-1","extname":".png","path":"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-04-24-openrtmfp-cumulus-4-1.png"},{"basename":"2012-04-24-openrtmfp-cumulus-4-2","extname":".png","path":"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-04-24-openrtmfp-cumulus-4-2.png"},{"basename":"2012-06-07-openrtmfp-cumulus-6-1","extname":".png","path":"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-07-openrtmfp-cumulus-6-1.png"},{"basename":"2012-06-07-openrtmfp-cumulus-6-2","extname":".png","path":"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-07-openrtmfp-cumulus-6-2.png"},{"basename":"2012-06-07-openrtmfp-cumulus-6-3","extname":".png","path":"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-07-openrtmfp-cumulus-6-3.png"},{"basename":"2012-06-25-openrtmfp-cumulus-7-1","extname":".png","path":"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-25-openrtmfp-cumulus-7-1.png"},{"basename":"2012-06-25-openrtmfp-cumulus-7-2","extname":".png","path":"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-25-openrtmfp-cumulus-7-2.png"},{"basename":"2012-06-25-openrtmfp-cumulus-7-3","extname":".png","path":"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-25-openrtmfp-cumulus-7-3.png"},{"basename":"2020-04-15-covid2019-catering-business-mode-1","extname":".jpg","path":"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-04-15-covid2019-catering-business-mode-1.jpg"},{"basename":"2020-06-11-captain-alibaba-1","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-1.png"},{"basename":"2020-06-11-captain-alibaba-2","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-2.png"},{"basename":"2020-06-11-captain-alibaba-3","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-3.png"},{"basename":"2020-06-11-captain-alibaba-4","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-4.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-4.png"},{"basename":"2020-06-11-captain-alibaba-5","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-5.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-5.png"},{"basename":"2020-06-11-captain-alibaba-6","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-6.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-6.png"},{"basename":"2020-11-11-captain-double-eleven-1","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-1.jpg"},{"basename":"2020-11-11-captain-double-eleven-10","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-10.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-10.jpg"},{"basename":"2020-11-11-captain-double-eleven-11","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-11.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-11.jpg"},{"basename":"2020-11-11-captain-double-eleven-2","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-2.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-2.jpg"},{"basename":"2020-11-11-captain-double-eleven-3","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-3.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-3.jpg"},{"basename":"2020-11-11-captain-double-eleven-4","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-4.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-4.jpg"},{"basename":"2020-11-11-captain-double-eleven-5","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-5.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-5.jpg"},{"basename":"2020-11-11-captain-double-eleven-6","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-6.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-6.jpg"},{"basename":"2020-11-11-captain-double-eleven-7","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-7.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-7.jpg"},{"basename":"2020-11-11-captain-double-eleven-8","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-8.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-8.jpg"},{"basename":"2020-11-11-captain-double-eleven-9","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-9.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-9.jpg"},{"basename":"2021-11-11-captain-tttm-1","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-1.jpg"},{"basename":"2021-11-11-captain-tttm-10","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-10.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-10.jpg"},{"basename":"2021-11-11-captain-tttm-11","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-11.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-11.jpg"},{"basename":"2021-11-11-captain-tttm-2","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-2.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-2.jpg"},{"basename":"2021-11-11-captain-tttm-3","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-3.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-3.jpg"},{"basename":"2021-11-11-captain-tttm-4","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-4.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-4.jpg"},{"basename":"2021-11-11-captain-tttm-5","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-5.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-5.jpg"},{"basename":"2021-11-11-captain-tttm-6","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-6.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-6.jpg"},{"basename":"2021-11-11-captain-tttm-7","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-7.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-7.jpg"},{"basename":"2021-11-11-captain-tttm-8","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-8.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-8.jpg"},{"basename":"2021-11-11-captain-tttm-9","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-9.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-9.jpg"},{"basename":"2022-07-27-captain-birthday-1","extname":".png","path":"/img/src/2022-07-27-captain-birthday-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-07-27-captain-birthday-1.png"},{"basename":"2022-12-11-wechat-chatgpt-1","extname":".png","path":"/img/src/2022-12-11-wechat-chatgpt-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-11-wechat-chatgpt-1.png"},{"basename":"2022-12-11-wechat-chatgpt-2","extname":".png","path":"/img/src/2022-12-11-wechat-chatgpt-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-11-wechat-chatgpt-2.png"},{"basename":"2022-12-11-wechat-chatgpt-3","extname":".png","path":"/img/src/2022-12-11-wechat-chatgpt-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-11-wechat-chatgpt-3.png"},{"basename":"2022-12-16-midjourney-first-test-1","extname":".png","path":"/img/src/2022-12-16-midjourney-first-test-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-16-midjourney-first-test-1.png"},{"basename":"2022-12-16-midjourney-first-test-2","extname":".png","path":"/img/src/2022-12-16-midjourney-first-test-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-16-midjourney-first-test-2.png"},{"basename":"2022-12-16-midjourney-first-test-3","extname":".png","path":"/img/src/2022-12-16-midjourney-first-test-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-16-midjourney-first-test-3.png"},{"basename":"2022-12-17-ai-bert-1-1","extname":".jpg","path":"/img/src/2022-12-17-ai-bert-1-1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-17-ai-bert-1-1.jpg"},{"basename":"2022-12-21-build-github-pages-with-jekyll-1","extname":".png","path":"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-21-build-github-pages-with-jekyll-1.png"},{"basename":"2022-12-21-build-github-pages-with-jekyll-2","extname":".png","path":"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-21-build-github-pages-with-jekyll-2.png"},{"basename":"2022-12-24-captain-nlp-1","extname":".png","path":"/img/src/2022-12-24-captain-nlp-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-1.png"},{"basename":"2022-12-24-captain-nlp-2","extname":".jpg","path":"/img/src/2022-12-24-captain-nlp-2.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-2.jpg"},{"basename":"2022-12-24-captain-nlp-3","extname":".jpg","path":"/img/src/2022-12-24-captain-nlp-3.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-3.jpg"},{"basename":"2022-12-24-captain-nlp-4","extname":".png","path":"/img/src/2022-12-24-captain-nlp-4.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-4.png"},{"basename":"2022-12-24-captain-nlp-5","extname":".png","path":"/img/src/2022-12-24-captain-nlp-5.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-5.png"},{"basename":"2022-12-24-captain-nlp-6","extname":".png","path":"/img/src/2022-12-24-captain-nlp-6.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-6.png"},{"basename":"2022-12-24-captain-nlp-7","extname":".png","path":"/img/src/2022-12-24-captain-nlp-7.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-7.png"},{"basename":"2022-12-24-captain-nlp-8","extname":".png","path":"/img/src/2022-12-24-captain-nlp-8.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-8.png"},{"basename":"2022-12-25-captain-cv-1","extname":".jpeg","path":"/img/src/2022-12-25-captain-cv-1.jpeg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-25-captain-cv-1.jpeg"},{"basename":"main","extname":".js","path":"/js/main.js","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"main.js"}],"categories":{"rt_tech":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</h2>\t\t\n\t<time datetime=\"2012-08-04T17:58:17+00:00\" class=\"by-line\">04 Aug 2012, 广州 | 作者 麦克船长 | 总计 5236 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一rtmfpserver-线程的启动和等待\" id=\"markdown-toc-一rtmfpserver-线程的启动和等待\">一、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的启动和等待</a> <ul>\n <li><a href=\"#1pocothread\" id=\"markdown-toc-1pocothread\">1、<code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code></a></li>\n <li><a href=\"#2封装一个可运行线程的类\" id=\"markdown-toc-2封装一个可运行线程的类\">2、封装一个可运行线程的类</a></li>\n <li><a href=\"#3启动-rtmfpserver-线程\" id=\"markdown-toc-3启动-rtmfpserver-线程\">3、启动 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</a></li>\n <li><a href=\"#4rtmfpserver-线程等待\" id=\"markdown-toc-4rtmfpserver-线程等待\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程等待</a></li>\n </ul>\n </li>\n <li><a href=\"#二rtmfpmanager-对-rtmfpserver-的影响\" id=\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\">二、<code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的影响</a></li>\n</ul>\n\n<h3 id=\"一rtmfpserver-线程的启动和等待\">一、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的启动和等待</h3>\n\n<h4 id=\"1pocothread\">1、<code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code></h4>\n\n<p>Cumulus 大量使用了 <code class=\"language-plaintext highlighter-rouge\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">PoechantRunnable</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Runnable</span> <span class=\"p\">{</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"c1\">// your codes</span>\n <span class=\"p\">}</span>\n<span class=\"p\">};</span>\n \n<span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">PoechantRunnable</span> <span class=\"n\">runnable</span><span class=\"p\">;</span> <span class=\"c1\">// Image that it's a gift</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Thread</span> <span class=\"kr\">thread</span><span class=\"p\">;</span> <span class=\"c1\">// And… thread is just like your girl</span>\n <span class=\"kr\">thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">runnable</span><span class=\"p\">);</span> <span class=\"c1\">// Okay, give your sweet babe the gift :)</span>\n <span class=\"kr\">thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2封装一个可运行线程的类\">2、封装一个可运行线程的类</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中实现了一个 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 类,该类继承了 <code class=\"language-plaintext highlighter-rouge\">Runnable</code>,就是上面那个 <code class=\"language-plaintext highlighter-rouge\">gift</code> 喽。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">StartableProcess</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Runnable</span><span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">StartableProcess</span><span class=\"p\">(</span><span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">startable</span><span class=\"p\">);</span>\n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">run</span><span class=\"p\">();</span>\n <span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">_startable</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>可以看到其中有 <code class=\"language-plaintext highlighter-rouge\">Startable& _startable</code> 引用成员,它并没有继承 <code class=\"language-plaintext highlighter-rouge\">Runnable</code>,而是封装了 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 和 <code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Thread</span> <span class=\"kr\">_thread</span><span class=\"p\">;</span>\n<span class=\"n\">StartableProcess</span> <span class=\"n\">_process</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>这里 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 封装了一个 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 成员,与 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\n\n<h4 id=\"3启动-rtmfpserver-线程\">3、启动 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</h4>\n<p>我们可以看到在 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的构造函数中初始化了 <code class=\"language-plaintext highlighter-rouge\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\"language-plaintext highlighter-rouge\">(Flag Field)_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">true</code>,因为它还没有调用启动函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_name</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">),</span>\n <span class=\"kr\">_thread</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">),</span>\n <span class=\"n\">_stop</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_haveToJoin</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">_process</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>初始化 <code class=\"language-plaintext highlighter-rouge\">_process</code> 时,调用 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 构造函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">StartableProcess</span><span class=\"o\">::</span><span class=\"n\">StartableProcess</span><span class=\"p\">(</span><span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">startable</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_startable</span><span class=\"p\">(</span><span class=\"n\">startable</span><span class=\"p\">){</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>传入 <code class=\"language-plaintext highlighter-rouge\">_startable</code> 的引用。在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的,然后通过调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 来启动,启动后会响应到 <code class=\"language-plaintext highlighter-rouge\">run()</code>。下面我们以 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程为例。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 类是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">RTMFPServer</span>\n <span class=\"o\">:</span> <span class=\"k\">private</span> <span class=\"n\">Gateway</span><span class=\"p\">,</span>\n <span class=\"k\">protected</span> <span class=\"n\">Handler</span><span class=\"p\">,</span>\n <span class=\"k\">private</span> <span class=\"n\">Startable</span><span class=\"p\">,</span>\n <span class=\"k\">private</span> <span class=\"n\">SocketHandler</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的构造函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">RTMFPServer</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">cores</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer\"</span><span class=\"p\">),</span>\n <span class=\"n\">_sendingEngine</span><span class=\"p\">(</span><span class=\"n\">cores</span><span class=\"p\">),</span>\n <span class=\"n\">_receivingEngine</span><span class=\"p\">(</span><span class=\"n\">cores</span><span class=\"p\">),</span>\n <span class=\"n\">_pCirrus</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_handshake</span><span class=\"p\">(</span><span class=\"n\">_receivingEngine</span><span class=\"p\">,</span>\n <span class=\"n\">_sendingEngine</span><span class=\"p\">,</span>\n <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">),</span>\n <span class=\"n\">_sessions</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\n\n<table>\n <thead>\n <tr>\n <th>所在线程</th>\n <th>调用者</th>\n <th>函数</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>主线程</td>\n <td>main(…)</td>\n <td> </td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::start()</td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer对象</td>\n <td>Startable::start()</td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer从Startable继承来的Thread成员</td>\n <td>Thread::start(…)</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\n <td>StartableProcess::run()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::prerun()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>Startable::prerun()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::run()</td>\n </tr>\n </tbody>\n</table>\n\n<h4 id=\"4rtmfpserver-线程等待\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程等待</h4>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">terminate</span><span class=\"p\">)</span>\n <span class=\"n\">handle</span><span class=\"p\">(</span><span class=\"n\">terminate</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">handle(…)</code> 函数很简单,如下只进行了 <code class=\"language-plaintext highlighter-rouge\">sleep(...)</code> 和 <code class=\"language-plaintext highlighter-rouge\">giveHandle()</code> 两个操作。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">handle</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">){</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">sleep</span><span class=\"p\">()</span> <span class=\"o\">!=</span> <span class=\"n\">STOP</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">giveHandle</span><span class=\"p\">();</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span>\n <span class=\"n\">terminate</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">sleep(…)</code> 是 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 是从 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 继承而来的,声明如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">WakeUpType</span> <span class=\"nf\">sleep</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>定义如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">WakeUpType</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"n\">WakeUpType</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">WAKEUP</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">tryWait</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">))</span>\n <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">TIMEOUT</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>在运行状态下,<code class=\"language-plaintext highlighter-rouge\">_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">false</code>,而默认参数 <code class=\"language-plaintext highlighter-rouge\">timeout</code> 为 0,所以会调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>这个 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent</code> 成员是一个 <code class=\"language-plaintext highlighter-rouge\">Poco::Event</code> 对象,<code class=\"language-plaintext highlighter-rouge\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\"language-plaintext highlighter-rouge\">Poco::Event::wait()</code> 后,会一直等待 <code class=\"language-plaintext highlighter-rouge\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\"language-plaintext highlighter-rouge\">wait</code> 的状态。在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中 <code class=\"language-plaintext highlighter-rouge\">set</code> 的动作是由:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPServer::requestHandle()</code></li>\n <li><code class=\"language-plaintext highlighter-rouge\">PoolThread::push(Poco::AutoPtr<RunnableType>& pRunnable)</code></li>\n</ul>\n\n<p>执行的。</p>\n\n<h3 id=\"二rtmfpmanager-对-rtmfpserver-的影响\">二、<code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的影响</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 与 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 同样,继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">RTMFPManager</span> <span class=\"o\">:</span> <span class=\"k\">private</span> <span class=\"n\">Task</span><span class=\"p\">,</span> <span class=\"k\">private</span> <span class=\"n\">Startable</span>\n</code></pre></div></div>\n\n<p>在构造函数中将 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\"language-plaintext highlighter-rouge\">_server</code> 引用成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">RTMFPManager</span><span class=\"p\">(</span><span class=\"n\">RTMFPServer</span><span class=\"o\">&</span> <span class=\"n\">server</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_server</span><span class=\"p\">(</span><span class=\"n\">server</span><span class=\"p\">),</span>\n <span class=\"n\">Task</span><span class=\"p\">(</span><span class=\"n\">server</span><span class=\"p\">),</span>\n <span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPManager\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">start</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n\n<span class=\"cm\">/* ...... */</span>\n\n<span class=\"n\">RTMFPServer</span><span class=\"o\">&</span> <span class=\"n\">_server</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的构造函数中调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 成员函数,是从 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的线程。然后响应到 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager::run()</code> 函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">setPriority</span><span class=\"p\">(</span><span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">PRIO_LOW</span><span class=\"p\">);</span>\n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">2000</span><span class=\"p\">)</span><span class=\"o\">!=</span><span class=\"n\">STOP</span><span class=\"p\">)</span>\n <span class=\"n\">waitHandle</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这里要强调的是,这里的 <code class=\"language-plaintext highlighter-rouge\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\n\n<p>在这里我们可以看到 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的 <code class=\"language-plaintext highlighter-rouge\">handle(…)</code> 中的 <code class=\"language-plaintext highlighter-rouge\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的主循环的等待时间的。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">WakeUpType</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"n\">WakeUpType</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">WAKEUP</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">tryWait</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">))</span>\n <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">TIMEOUT</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>你可以自行修改 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 中 <code class=\"language-plaintext highlighter-rouge\">sleep(...)</code> 的参数,这样就会调用 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\"language-plaintext highlighter-rouge\">timeout</code>)来进行睡眠。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\"language-plaintext highlighter-rouge\">handle</code> 成员函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">handle</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_server</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这里就会调用到 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 源码时知道 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::manage()</code> 函数并不是在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程内运行的,而是 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">manage</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\"language-plaintext highlighter-rouge\">Session</code>。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</title>\n \t<meta name=\"description\" content=\"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</h2>\t\t\n\t<time datetime=\"2012-07-23T03:07:43+00:00\" class=\"by-line\">23 Jul 2012, 广州 | 作者 麦克船长 | 总计 3111 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#1客户端发布publishing-on-client-side\" id=\"markdown-toc-1客户端发布publishing-on-client-side\">1、客户端发布(Publishing on client side)</a></li>\n <li><a href=\"#2服务器端server-side\" id=\"markdown-toc-2服务器端server-side\">2、服务器端(Server-side)</a></li>\n <li><a href=\"#3客户端订阅subscribing-on-client-side\" id=\"markdown-toc-3客户端订阅subscribing-on-client-side\">3、客户端订阅(Subscribing on client side)</a></li>\n <li><a href=\"#4reference\" id=\"markdown-toc-4reference\">4、Reference</a></li>\n</ul>\n\n<p>整个流程概括如下:</p>\n\n<p>Flash 客户端通过 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 与 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 建立连接,然后通过 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\n\n<h3 id=\"1客户端发布publishing-on-client-side\">1、客户端发布(Publishing on client side)</h3>\n\n<p>通过 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\"/2012/04/10/openrtmfp-cumulus-1/\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\"language-plaintext highlighter-rouge\">nc</code> 是一个 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 对象。</p>\n\n<div class=\"language-actionscript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">nc</span><span class=\"p\">.</span><span class=\"nx\">connect</span><span class=\"p\">(</span><span class=\"s2\">\"rtmfp://localhost:1935\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\"language-plaintext highlighter-rouge\">ns1</code> 是一个 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 对象。</p>\n\n<div class=\"language-actionscript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">ns1</span><span class=\"p\">.</span><span class=\"nx\">publish</span><span class=\"p\">(</span><span class=\"s2\">\"poechant_media_flow\"</span><span class=\"p\">,</span> <span class=\"s2\">\"live\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\n\n<h3 id=\"2服务器端server-side\">2、服务器端(Server-side)</h3>\n\n<p>Cumulus 通过 <code class=\"language-plaintext highlighter-rouge\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">receive</span><span class=\"p\">(</span><span class=\"n\">RTMFPReceiving</span><span class=\"o\">&</span> <span class=\"n\">rtmfpReceiving</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\n\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\"language-plaintext highlighter-rouge\">Handshake</code> 类的 <code class=\"language-plaintext highlighter-rouge\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">ServerSession</span><span class=\"o\">::</span><span class=\"n\">packetHandler</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">packet</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Flow</span><span class=\"o\">::</span><span class=\"n\">fragmentSortedHandler</span><span class=\"p\">(</span><span class=\"n\">UInt64</span> <span class=\"n\">stage</span><span class=\"p\">,</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">fragment</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">flags</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">switch</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AMF_WITH_HANDLER</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AMF</span><span class=\"p\">:</span>\n <span class=\"n\">messageHandler</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">amf</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AUDIO</span><span class=\"p\">:</span>\n <span class=\"n\">audioHandler</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">VIDEO</span><span class=\"p\">:</span>\n <span class=\"n\">videoHandler</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"nl\">default:</span>\n <span class=\"n\">rawHandler</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>接下来在 <code class=\"language-plaintext highlighter-rouge\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span> <span class=\"o\">!=</span> <span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushAudioPacket</span><span class=\"p\">(</span><span class=\"n\">time</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n \n<span class=\"k\">for</span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span><span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span><span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushVideoPacket</span><span class=\"p\">(</span><span class=\"n\">time</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n \n<span class=\"k\">for</span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span><span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span><span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushDataPacket</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中的 <code class=\"language-plaintext highlighter-rouge\">_listeners</code> 就是该 <code class=\"language-plaintext highlighter-rouge\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Listener</span><span class=\"o\">&</span> <span class=\"n\">addListener</span><span class=\"p\">(</span>\n <span class=\"n\">Peer</span><span class=\"o\">&</span> <span class=\"n\">peer</span><span class=\"p\">,</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">id</span><span class=\"p\">,</span>\n <span class=\"n\">FlowWriter</span><span class=\"o\">&</span> <span class=\"n\">writer</span><span class=\"p\">,</span>\n <span class=\"kt\">bool</span> <span class=\"n\">unbuffered</span><span class=\"p\">);</span>\n \n<span class=\"kt\">void</span> <span class=\"nf\">removeListener</span><span class=\"p\">(</span>\n <span class=\"n\">Peer</span><span class=\"o\">&</span> <span class=\"n\">peer</span><span class=\"p\">,</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">id</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这两个函数来实现的。</p>\n\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\n\n<h3 id=\"3客户端订阅subscribing-on-client-side\">3、客户端订阅(Subscribing on client side)</h3>\n\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>ns2.play(\"poechant_media_flow\");\n</code></pre></div></div>\n\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\"language-plaintext highlighter-rouge\">NetStream::send(…)</code> 的,用于发送 <code class=\"language-plaintext highlighter-rouge\">Data</code>,<code class=\"language-plaintext highlighter-rouge\">Audio</code> 和 <code class=\"language-plaintext highlighter-rouge\">Video</code> 的程序可以参考该例修改。</p>\n\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 初始化的时候,传入 <code class=\"language-plaintext highlighter-rouge\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\"language-plaintext highlighter-rouge\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\n\n<h3 id=\"4reference\">4、Reference</h3>\n\n<ul>\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\n</ul>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</h2>\t\t\n\t<time datetime=\"2012-06-25T02:56:26+00:00\" class=\"by-line\">25 Jun 2012, 广州 | 作者 麦克船长 | 总计 2111 字</time>\n\t<div class=\"content\">\n\t\t<p><code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中的线程都是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code>,在其中封装 <code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\"language-plaintext highlighter-rouge\">Startable</code> 中的 <code class=\"language-plaintext highlighter-rouge\">start</code> 函数如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">)</span> <span class=\"c1\">// if running</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n \n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span>\n <span class=\"s\">\"Try to start up a new thread inherited from Startable\"</span><span class=\"p\">);</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">_process</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> \n <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span>\n <span class=\"s\">\"Impossible to start the thread : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样一个类继承 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code>,然后调用到该类自己的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\"language-plaintext highlighter-rouge\">SocketManager</code> 为例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">SocketManager</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"err\">…</span> \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"err\">…</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>我们要看看这个 <code class=\"language-plaintext highlighter-rouge\">running()</code> 是怎么回事,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">bool</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">running</span><span class=\"p\">()</span> <span class=\"k\">const</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>很简单,就是通过 <code class=\"language-plaintext highlighter-rouge\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 是什么时候被设置为 <code class=\"language-plaintext highlighter-rouge\">false</code> 的呢?就是上面的 <code class=\"language-plaintext highlighter-rouge\">start()</code>,这里存在的一个问题就是先 <code class=\"language-plaintext highlighter-rouge\">start</code> 线程,再设置 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_thread.start(_process);\n_stop=false;\n</code></pre></div></div>\n\n<p>而 <code class=\"language-plaintext highlighter-rouge\">start()</code> 之后 <code class=\"language-plaintext highlighter-rouge\">run()</code> 的时候就开始通过 <code class=\"language-plaintext highlighter-rouge\">running()</code> 来判断 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 值了。所以你会在使用 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\" alt=\"image\" /></p>\n\n<p>它们是:</p>\n\n<ul>\n <li>主线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 线程</li>\n</ul>\n\n<p>而异常情况可能是 <code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 没有启动,甚至 <code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 都没有启动。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\" alt=\"image\" /></p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 都没有启动的情况 T.T</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\" alt=\"image\" /></p>\n\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\n\n<p>解决办法就是将 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 值为 <code class=\"language-plaintext highlighter-rouge\">true</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">)</span> <span class=\"c1\">// if running</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n \n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span>\n <span class=\"s\">\"Try to start up a new thread inherited from Startable\"</span><span class=\"p\">);</span>\n <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">_process</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> \n <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span> \n <span class=\"c1\">// June 25th, 2012, Michael@YY</span>\n <span class=\"p\">}</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span>\n <span class=\"s\">\"Impossible to start the thread : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</h2>\t\t\n\t<time datetime=\"2012-06-07T15:34:18+00:00\" class=\"by-line\">07 Jun 2012, 广州 | 作者 麦克船长 | 总计 1538 字</time>\n\t<div class=\"content\">\n\t\t<p>OpenRTMFP/Cumulus 提供了 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>。</p>\n\n<p>一般来说,Thread A 会准备好要 <code class=\"language-plaintext highlighter-rouge\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\"language-plaintext highlighter-rouge\">push</code> 消息。</p>\n\n<p>但是 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\"language-plaintext highlighter-rouge\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\" alt=\"image\" /></p>\n\n<p>由于在 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\"language-plaintext highlighter-rouge\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\"language-plaintext highlighter-rouge\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\" alt=\"image\" /></p>\n\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">createAMFMessage</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">)</span>\n \n <span class=\"c1\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"p\">(</span><span class=\"n\">_closed</span> <span class=\"o\">||</span> <span class=\"n\">signature</span><span class=\"p\">.</span><span class=\"n\">empty</span><span class=\"p\">()</span> <span class=\"o\">||</span> <span class=\"n\">_band</span><span class=\"p\">.</span><span class=\"n\">failed</span><span class=\"p\">()))</span> <span class=\"p\">{</span>\n <span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">MessageBuffered</span><span class=\"p\">();</span>\n <span class=\"n\">MessageBuffered</span><span class=\"o\">&</span> <span class=\"n\">message</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"n\">writeResponseHeader</span><span class=\"p\">(</span><span class=\"n\">message</span><span class=\"p\">.</span><span class=\"n\">rawWriter</span><span class=\"p\">,</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">pMessage</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">MessageBuffered</span><span class=\"o\">&</span> <span class=\"n\">message</span><span class=\"p\">(</span><span class=\"n\">_MessageNull</span><span class=\"p\">);</span>\n <span class=\"n\">writeResponseHeader</span><span class=\"p\">(</span><span class=\"n\">message</span><span class=\"p\">.</span><span class=\"n\">rawWriter</span><span class=\"p\">,</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">NULL</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>然后再调用时最后再增加 <code class=\"language-plaintext highlighter-rouge\">push</code> 操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"kt\">void</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">pushAMFMessage</span><span class=\"p\">(</span><span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMessage</span> <span class=\"o\">!=</span> <span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_messages</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\" alt=\"image\" /></p>\n\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"kt\">void</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">pushAMFMessage</span><span class=\"p\">(</span><span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMessage</span> <span class=\"o\">!=</span> <span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Mutex</span><span class=\"o\">::</span><span class=\"n\">ScopedLock</span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">msgQueueMutex</span><span class=\"p\">);</span>\n <span class=\"n\">_messages</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样就基本解决了这个线程安全问题。</p>\n\n<p>另外,使用 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</h2>\t\t\n\t<time datetime=\"2012-04-24T03:31:10+00:00\" class=\"by-line\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 12668 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一流缓冲区\" id=\"markdown-toc-一流缓冲区\">一、流缓冲区</a> <ul>\n <li><a href=\"#1了解-stdstreambuf\" id=\"markdown-toc-1了解-stdstreambuf\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::streambuf</code></a> <ul>\n <li><a href=\"#11单步移动内置指针\" id=\"markdown-toc-11单步移动内置指针\">1.1、单步移动内置指针</a></li>\n <li><a href=\"#12获取-get-指针和-put-指针的位置\" id=\"markdown-toc-12获取-get-指针和-put-指针的位置\">1.2、获取 get 指针和 put 指针的位置</a></li>\n <li><a href=\"#13设置-get-和-put-指针可达区域的上下界\" id=\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\">1.3、设置 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针可达区域的上下界</a></li>\n </ul>\n </li>\n <li><a href=\"#2memorystreambuf\" id=\"markdown-toc-2memorystreambuf\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code></a> <ul>\n <li><a href=\"#21移动内置的-get-和-put-指针\" id=\"markdown-toc-21移动内置的-get-和-put-指针\">2.1、移动内置的 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针:</a></li>\n <li><a href=\"#22获取-get-和-put-指针当前位置\" id=\"markdown-toc-22获取-get-和-put-指针当前位置\">2.2、获取 get 和 put 指针当前位置:</a></li>\n <li><a href=\"#23获取缓冲区的起始位置和大小\" id=\"markdown-toc-23获取缓冲区的起始位置和大小\">2.3、获取缓冲区的起始位置和大小:</a></li>\n <li><a href=\"#24缓冲区的已写字节数\" id=\"markdown-toc-24缓冲区的已写字节数\">2.4、缓冲区的已写字节数</a></li>\n <li><a href=\"#25显式设定-put-和-get-指针位置\" id=\"markdown-toc-25显式设定-put-和-get-指针位置\">2.5、显式设定 <code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针位置</a></li>\n <li><a href=\"#26-修改缓冲区大小\" id=\"markdown-toc-26-修改缓冲区大小\">2.6 修改缓冲区大小</a></li>\n <li><a href=\"#27构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-27构造函数拷贝构造函数和析构函数\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#二io-流\" id=\"markdown-toc-二io-流\">二、IO 流</a> <ul>\n <li><a href=\"#1了解-stdios\" id=\"markdown-toc-1了解-stdios\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::ios</code></a></li>\n <li><a href=\"#2memoryios\" id=\"markdown-toc-2memoryios\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code></a> <ul>\n <li><a href=\"#21构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-21构造函数拷贝构造函数和析构函数\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\n <li><a href=\"#22得到-memorystreambuf-成员的地址\" id=\"markdown-toc-22得到-memorystreambuf-成员的地址\">2.2、得到 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的地址</a></li>\n <li><a href=\"#23当前位置\" id=\"markdown-toc-23当前位置\">2.3、当前位置</a></li>\n <li><a href=\"#24封装-memorystreambuf-成员的一些函数\" id=\"markdown-toc-24封装-memorystreambuf-成员的一些函数\">2.4、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的一些函数</a></li>\n <li><a href=\"#25-缓冲区可读数据的字节数\" id=\"markdown-toc-25-缓冲区可读数据的字节数\">2.5 缓冲区可读数据的字节数</a></li>\n </ul>\n </li>\n <li><a href=\"#3输入流\" id=\"markdown-toc-3输入流\">3、输入流</a></li>\n <li><a href=\"#4输出流\" id=\"markdown-toc-4输出流\">4、输出流</a> <ul>\n <li><a href=\"#41-构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-41-构造函数拷贝构造函数和析构函数\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\n <li><a href=\"#42-读取和设定已写字节数\" id=\"markdown-toc-42-读取和设定已写字节数\">4.2 读取和设定已写字节数</a></li>\n <li><a href=\"#43-当前位置\" id=\"markdown-toc-43-当前位置\">4.3 当前位置</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#三局部内存片\" id=\"markdown-toc-三局部内存片\">三、局部内存片</a> <ul>\n <li><a href=\"#1构造函数\" id=\"markdown-toc-1构造函数\">1、构造函数</a></li>\n <li><a href=\"#2析构函数\" id=\"markdown-toc-2析构函数\">2、析构函数</a></li>\n <li><a href=\"#3缓冲区切割\" id=\"markdown-toc-3缓冲区切割\">3、缓冲区切割</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\n\n<h3 id=\"一流缓冲区\">一、流缓冲区</h3>\n\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\n\n<h4 id=\"1了解-stdstreambuf\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::streambuf</code></h4>\n\n<p>首先要了解 <code class=\"language-plaintext highlighter-rouge\">streambuf</code> 内置了一个 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针和一个 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针。<code class=\"language-plaintext highlighter-rouge\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\"language-plaintext highlighter-rouge\">g</code> 和 <code class=\"language-plaintext highlighter-rouge\">p</code> 就分别表示 get pointer 和 put pointer。</p>\n\n<h5 id=\"11单步移动内置指针\">1.1、单步移动内置指针</h5>\n\n<p>Increase get pointer: Advances the get pointer by <code class=\"language-plaintext highlighter-rouge\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">gbump</span> <span class=\"p\">(</span> <span class=\"kt\">int</span> <span class=\"n\">n</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>Increase put pointer: Advances the put pointer by <code class=\"language-plaintext highlighter-rouge\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">pbump</span> <span class=\"p\">(</span> <span class=\"kt\">int</span> <span class=\"n\">n</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<h5 id=\"12获取-get-指针和-put-指针的位置\">1.2、获取 get 指针和 put 指针的位置</h5>\n\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">char</span> <span class=\"o\">*</span> <span class=\"n\">gptr</span> <span class=\"p\">(</span> <span class=\"p\">)</span> <span class=\"k\">const</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">char</span> <span class=\"o\">*</span> <span class=\"n\">pptr</span> <span class=\"p\">(</span> <span class=\"p\">)</span> <span class=\"k\">const</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h5 id=\"13设置-get-和-put-指针可达区域的上下界\">1.3、设置 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针可达区域的上下界</h5>\n\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">setg</span> <span class=\"p\">(</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gbeg</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gnext</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gend</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\n</ul>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">setp</span> <span class=\"p\">(</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pbeg</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pend</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\n</ul>\n\n<h4 id=\"2memorystreambuf\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code></h4>\n\n<p>类定义:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryStreamBuf</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">streambuf</span> <span class=\"p\">{</span>\n <span class=\"k\">friend</span> <span class=\"k\">class</span> <span class=\"nc\">ScopedMemoryClip</span><span class=\"p\">;</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">written</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gCurrent</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pCurrent</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n \n<span class=\"nl\">private:</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">overflow</span><span class=\"p\">(</span><span class=\"n\">int_type</span> <span class=\"n\">c</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">underflow</span><span class=\"p\">();</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">sync</span><span class=\"p\">();</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_written</span><span class=\"p\">;</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">();</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"k\">operator</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span><span class=\"p\">);</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 是 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\n\n<h5 id=\"21移动内置的-get-和-put-指针\">2.1、移动内置的 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针:</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针都移动:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">gbump</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22获取-get-和-put-指针当前位置\">2.2、获取 get 和 put 指针当前位置:</h5>\n\n<p>封装 <code class=\"language-plaintext highlighter-rouge\">streambuf</code> 的 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 和 <code class=\"language-plaintext highlighter-rouge\">pptr</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">gCurrent</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">gptr</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">pptr</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23获取缓冲区的起始位置和大小\">2.3、获取缓冲区的起始位置和大小:</h5>\n\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"24缓冲区的已写字节数\">2.4、缓冲区的已写字节数</h5>\n\n<p>读取(其中也可能发生设置操作):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"kt\">int</span> <span class=\"n\">written</span> <span class=\"o\">=</span> <span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"c1\">// 已写字节数</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">written</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">written</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">written</span> <span class=\"o\">></span> <span class=\"n\">_written</span><span class=\"p\">)</span> <span class=\"c1\">// 保存已写字节数</span>\n <span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"p\">)</span><span class=\"n\">written</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">_written</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设置:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_written</span><span class=\"o\">=</span><span class=\"n\">size</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"25显式设定-put-和-get-指针位置\">2.5、显式设定 <code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针位置</h5>\n\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"c1\">// 保存已写字节数</span>\n <span class=\"n\">written</span><span class=\"p\">();</span> <span class=\"c1\">// Save nb char written</span>\n \n <span class=\"c1\">// 移动 put 指针</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n <span class=\"n\">pbump</span><span class=\"p\">((</span><span class=\"kt\">int</span><span class=\"p\">)</span> <span class=\"n\">pos</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 移动 get 指针</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">pos</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"26-修改缓冲区大小\">2.6 修改缓冲区大小</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"c1\">// 大小标识</span>\n <span class=\"n\">_bufferSize</span> <span class=\"o\">=</span> <span class=\"n\">newSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// gptr 当前位置</span>\n <span class=\"kt\">int</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">gCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 gptr 可达范围和当前位置</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">pos</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span> \n <span class=\"c1\">// pptr 当前位置</span>\n <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 pptr 可达范围和当前位置</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"27构造函数拷贝构造函数和析构函数\">2.7、构造函数、拷贝构造函数和析构函数</h5>\n\n<p>构造函数会设定 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 和 <code class=\"language-plaintext highlighter-rouge\">gptr</code>,并初始化 <code class=\"language-plaintext highlighter-rouge\">pBuffer</code> 和 <code class=\"language-plaintext highlighter-rouge\">bufferSize</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> <span class=\"n\">_pBuffer</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">),</span><span class=\"n\">_bufferSize</span><span class=\"p\">(</span><span class=\"n\">bufferSize</span><span class=\"p\">),</span><span class=\"n\">_written</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span><span class=\"p\">,</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构函数会拷贝对方的 <code class=\"language-plaintext highlighter-rouge\">pBuffer</code>、<code class=\"language-plaintext highlighter-rouge\">bufferSizse</code>、<code class=\"language-plaintext highlighter-rouge\">_written</code>,并设定 <code class=\"language-plaintext highlighter-rouge\">gptr</code>、<code class=\"language-plaintext highlighter-rouge\">pptr</code>。注意设定 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 时,要分别调用 <code class=\"language-plaintext highlighter-rouge\">setp</code> 和 <code class=\"language-plaintext highlighter-rouge\">pbump</code>,因为 <code class=\"language-plaintext highlighter-rouge\">setp</code> 仅将 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span> <span class=\"n\">_pBuffer</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">),</span><span class=\"n\">_bufferSize</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">),</span><span class=\"n\">_written</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_written</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">gCurrent</span><span class=\"p\">(),</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">pbump</span><span class=\"p\">((</span><span class=\"kt\">int</span><span class=\"p\">)(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"n\">_pBuffer</span><span class=\"p\">));</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::~</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"二io-流\">二、IO 流</h3>\n\n<h4 id=\"1了解-stdios\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::ios</code></h4>\n\n<p>Initialize object [<code class=\"language-plaintext highlighter-rouge\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">init</span> <span class=\"p\">(</span> <span class=\"n\">streambuf</span><span class=\"o\">*</span> <span class=\"n\">sb</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>初始化后如下函数的返回值:</p>\n\n<table>\n <thead>\n <tr>\n <th>member function</th>\n <th>value</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>rdbuf()</td>\n <td>sb</td>\n </tr>\n <tr>\n <td>tie()</td>\n <td>0</td>\n </tr>\n <tr>\n <td>rdstate()</td>\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\n </tr>\n <tr>\n <td>exceptions()</td>\n <td>goodbit</td>\n </tr>\n <tr>\n <td>flags()</td>\n <td>skipws | dec</td>\n </tr>\n <tr>\n <td>width()</td>\n <td>0</td>\n </tr>\n <tr>\n <td>precision()</td>\n <td>6</td>\n </tr>\n <tr>\n <td>fill()</td>\n <td>‘ ’ (whitespace)</td>\n </tr>\n <tr>\n <td>getloc()</td>\n <td>a copy of locale()</td>\n </tr>\n </tbody>\n</table>\n\n<h4 id=\"2memoryios\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code></h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code>,且是 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 和 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\"language-plaintext highlighter-rouge\">std::ios</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryIOS</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"k\">virtual</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ios</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">MemoryIOS</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryIOS</span><span class=\"p\">();</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">*</span> <span class=\"n\">rdbuf</span><span class=\"p\">();</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">);</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"n\">MemoryStreamBuf</span> <span class=\"n\">_buf</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"21构造函数拷贝构造函数和析构函数\">2.1、构造函数、拷贝构造函数和析构函数</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span><span class=\"n\">_buf</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">poco_ios_init</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">poco_ios_init</code> 为 <code class=\"language-plaintext highlighter-rouge\">init</code> 的宏定义,用于初始化成员 <code class=\"language-plaintext highlighter-rouge\">_buf</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">MemoryIOS</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span><span class=\"n\">_buf</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_buf</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">poco_ios_init</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::~</span><span class=\"n\">MemoryIOS</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22得到-memorystreambuf-成员的地址\">2.2、得到 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的地址</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">*</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">rdbuf</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23当前位置\">2.3、当前位置</h5>\n\n<p>这是一个纯虚函数,由 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 和 <code class=\"language-plaintext highlighter-rouge\">MemoryOutpuStream</code> 继承时实现:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">virtual</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h5 id=\"24封装-memorystreambuf-成员的一些函数\">2.4、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的一些函数</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">resize</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">newSize</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code> 封装为 <code class=\"language-plaintext highlighter-rouge\">reset</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"o\">>=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"25-缓冲区可读数据的字节数\">2.5 缓冲区可读数据的字节数</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"kt\">int</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"p\">(</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">begin</span><span class=\"p\">());</span> <span class=\"c1\">// 缓冲区剩余可读数据字节数</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">result</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"p\">)</span><span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3输入流\">3、输入流</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryInputStream</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">MemoryIOS</span><span class=\"p\">,</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">istream</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"c1\">/// Creates a MemoryInputStream for the given memory area,</span>\n <span class=\"c1\">/// ready for reading.</span>\n <span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryInputStream</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">();</span>\n <span class=\"c1\">/// Destroys the MemoryInputStream.</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 以及 <code class=\"language-plaintext highlighter-rouge\">istream</code>。<code class=\"language-plaintext highlighter-rouge\">istream</code> 是 <code class=\"language-plaintext highlighter-rouge\">iostream</code> 中的 <code class=\"language-plaintext highlighter-rouge\">basic_istream</code> 别名(<code class=\"language-plaintext highlighter-rouge\">typedef</code>)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> \n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"k\">const_cast</span><span class=\"o\"><</span><span class=\"kt\">char</span><span class=\"o\">*></span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">),</span> <span class=\"n\">bufferSize</span><span class=\"p\">),</span> <span class=\"n\">istream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryInputStream</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">),</span> <span class=\"n\">istream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryInputStream</span><span class=\"o\">::~</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>唯一的一个成员函数是 <code class=\"language-plaintext highlighter-rouge\">current</code>,封装了 <code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 的 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的 <code class=\"language-plaintext highlighter-rouge\">gCurrent</code> 函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">gCurrent</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4输出流\">4、输出流</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryOutputStream</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">MemoryIOS</span><span class=\"p\">,</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ostream</span>\n <span class=\"c1\">/// An input stream for reading from a memory area.</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"c1\">/// Creates a MemoryOutputStream for the given memory area,</span>\n <span class=\"c1\">/// ready for writing.</span>\n <span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryOutputStream</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">();</span>\n <span class=\"c1\">/// Destroys the MemoryInputStream.</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">written</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"41-构造函数拷贝构造函数和析构函数\">4.1 构造函数、拷贝构造函数和析构函数</h5>\n\n<p>如下,不赘述了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> \n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">bufferSize</span><span class=\"p\">),</span> <span class=\"n\">ostream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n<span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryOutputStream</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">),</span> <span class=\"n\">ostream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryOutputStream</span><span class=\"o\">::~</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(){</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"42-读取和设定已写字节数\">4.2 读取和设定已写字节数</h5>\n\n<p>读取:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">written</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"43-当前位置\">4.3 当前位置</h5>\n\n<p>与 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 中的封装类似:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">pCurrent</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三局部内存片\">三、局部内存片</h3>\n\n<p>在第一部分的流缓冲区介绍 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 中有一个 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ScopedMemoryClip</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">ScopedMemoryClip</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">offset</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Int32</span> <span class=\"n\">offset</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_offset</span><span class=\"p\">;</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">_buffer</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"1构造函数\">1、构造函数</h4>\n\n<p>构造函数传入的参数对应的就是 <code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\"language-plaintext highlighter-rouge\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">offset</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_offset</span><span class=\"p\">(</span><span class=\"n\">offset</span><span class=\"p\">),</span> <span class=\"n\">_buffer</span><span class=\"p\">(</span><span class=\"n\">buffer</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_offset</span> <span class=\"o\">>=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">_offset</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span> <span class=\"o\">-</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_offset</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_offset</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">_offset</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2析构函数\">2、析构函数</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::~</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"o\">-</span><span class=\"p\">(</span><span class=\"n\">Int32</span><span class=\"p\">)</span><span class=\"n\">_offset</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3缓冲区切割\">3、缓冲区切割</h4>\n\n<p>可以看到构造函数和析构函数中都调用了 <code class=\"language-plaintext highlighter-rouge\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\n\n<ul>\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\n</ul>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::</span><span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">Int32</span> <span class=\"n\">offset</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"c1\">// 获取到 gptr</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gpos</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">gCurrent</span><span class=\"p\">();</span>\n \n <span class=\"c1\">// 偏移缓冲区地址,并修改缓冲区大小</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span> <span class=\"o\">-=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\n <span class=\"kt\">int</span> <span class=\"n\">ppos</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 gptr 可达区域和位置</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">gpos</span><span class=\"p\">,</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 设置 pptr 可达区域和位置</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">ppos</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\"><</span> <span class=\"n\">offset</span><span class=\"p\">)</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\n <span class=\"k\">else</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">-=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">></span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</h2>\t\t\n\t<time datetime=\"2012-04-24T02:04:55+00:00\" class=\"by-line\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 30820 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一amf-数据类型定义\" id=\"markdown-toc-一amf-数据类型定义\">一、AMF 数据类型定义</a> <ul>\n <li><a href=\"#1数据类型\" id=\"markdown-toc-1数据类型\">1、数据类型</a></li>\n <li><a href=\"#2undefined-type\" id=\"markdown-toc-2undefined-type\">2、<code class=\"language-plaintext highlighter-rouge\">undefined</code> Type</a></li>\n <li><a href=\"#3null-type\" id=\"markdown-toc-3null-type\">3、<code class=\"language-plaintext highlighter-rouge\">null</code> Type</a></li>\n <li><a href=\"#4false-type\" id=\"markdown-toc-4false-type\">4、<code class=\"language-plaintext highlighter-rouge\">false</code> type</a></li>\n <li><a href=\"#5true-type\" id=\"markdown-toc-5true-type\">5、<code class=\"language-plaintext highlighter-rouge\">true</code> type</a></li>\n <li><a href=\"#6integer-type\" id=\"markdown-toc-6integer-type\">6、<code class=\"language-plaintext highlighter-rouge\">integer</code> type</a></li>\n <li><a href=\"#7double-type\" id=\"markdown-toc-7double-type\">7、<code class=\"language-plaintext highlighter-rouge\">double</code> type</a></li>\n <li><a href=\"#8string-type\" id=\"markdown-toc-8string-type\">8、<code class=\"language-plaintext highlighter-rouge\">String</code> type</a></li>\n <li><a href=\"#9xmldocument-type\" id=\"markdown-toc-9xmldocument-type\">9、<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> type</a></li>\n <li><a href=\"#10date-type\" id=\"markdown-toc-10date-type\">10、<code class=\"language-plaintext highlighter-rouge\">Date</code> type</a></li>\n <li><a href=\"#11array-type\" id=\"markdown-toc-11array-type\">11、<code class=\"language-plaintext highlighter-rouge\">Array</code> type</a></li>\n <li><a href=\"#12object-type\" id=\"markdown-toc-12object-type\">12、<code class=\"language-plaintext highlighter-rouge\">Object</code> type</a></li>\n <li><a href=\"#13xml-type\" id=\"markdown-toc-13xml-type\">13、<code class=\"language-plaintext highlighter-rouge\">XML</code> type</a></li>\n <li><a href=\"#14bytearray-type\" id=\"markdown-toc-14bytearray-type\">14、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> type</a></li>\n <li><a href=\"#15amf3-的使用\" id=\"markdown-toc-15amf3-的使用\">15、AMF3 的使用</a> <ul>\n <li><a href=\"#151netconnection-and-amf-3\" id=\"markdown-toc-151netconnection-and-amf-3\">15.1、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> and AMF 3</a></li>\n <li><a href=\"#152netconnection-in-actionscript-30\" id=\"markdown-toc-152netconnection-in-actionscript-30\">15.2、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> in ActionScript 3.0</a></li>\n <li><a href=\"#153bytearray-idatainput-and-idataoutput\" id=\"markdown-toc-153bytearray-idatainput-and-idataoutput\">15.3、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code>, <code class=\"language-plaintext highlighter-rouge\">IDataInput</code> and <code class=\"language-plaintext highlighter-rouge\">IDataOutput</code></a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#二binaryreaderwriter\" id=\"markdown-toc-二binaryreaderwriter\">二、<code class=\"language-plaintext highlighter-rouge\">BinaryReader/Writer</code></a> <ul>\n <li><a href=\"#1amf3-数据格式基础\" id=\"markdown-toc-1amf3-数据格式基础\">1、AMF3 数据格式基础</a></li>\n <li><a href=\"#2序列化\" id=\"markdown-toc-2序列化\">2、序列化</a></li>\n <li><a href=\"#3反序列化\" id=\"markdown-toc-3反序列化\">3、反序列化</a></li>\n </ul>\n </li>\n <li><a href=\"#三packetreaderwriter\" id=\"markdown-toc-三packetreaderwriter\">三、<code class=\"language-plaintext highlighter-rouge\">PacketReader/Writer</code></a> <ul>\n <li><a href=\"#1packetreader\" id=\"markdown-toc-1packetreader\">1、PacketReader</a> <ul>\n <li><a href=\"#11封装-memoryinputstream\" id=\"markdown-toc-11封装-memoryinputstream\">1.1、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code></a></li>\n <li><a href=\"#12收缩缓冲区\" id=\"markdown-toc-12收缩缓冲区\">1.2、收缩缓冲区</a></li>\n <li><a href=\"#13构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-13构造函数拷贝构造函数和析构函数\">1.3、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n <li><a href=\"#2packetwriter\" id=\"markdown-toc-2packetwriter\">2、<code class=\"language-plaintext highlighter-rouge\">PacketWriter</code></a> <ul>\n <li><a href=\"#21封装memoryoutputstream\" id=\"markdown-toc-21封装memoryoutputstream\">2.1、封装<code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code></a></li>\n <li><a href=\"#22封装-binarywriter\" id=\"markdown-toc-22封装-binarywriter\">2.2、封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code></a></li>\n <li><a href=\"#23构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-23构造函数拷贝构造函数和析构函数\">2.3、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四amfreader\" id=\"markdown-toc-四amfreader\">四、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code></a> <ul>\n <li><a href=\"#1objectdef\" id=\"markdown-toc-1objectdef\">1、<code class=\"language-plaintext highlighter-rouge\">ObjectDef</code></a></li>\n <li><a href=\"#2amfreader-定义\" id=\"markdown-toc-2amfreader-定义\">2、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 定义</a> <ul>\n <li><a href=\"#21构造函数析构函数\" id=\"markdown-toc-21构造函数析构函数\">2.1、构造函数、析构函数</a></li>\n <li><a href=\"#22简单封装-packetreader-的一些函数\" id=\"markdown-toc-22简单封装-packetreader-的一些函数\">2.2、简单封装 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 的一些函数</a></li>\n <li><a href=\"#23设置-gptr-位置\" id=\"markdown-toc-23设置-gptr-位置\">2.3、设置 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 位置</a></li>\n <li><a href=\"#24判断类型\" id=\"markdown-toc-24判断类型\">2.4、判断类型</a></li>\n </ul>\n </li>\n <li><a href=\"#3解析-as3-null\" id=\"markdown-toc-3解析-as3-null\">3、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Null</code></a></li>\n <li><a href=\"#4解析-as3-number\" id=\"markdown-toc-4解析-as3-number\">4、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Number</code></a></li>\n <li><a href=\"#5解析-as3-integer\" id=\"markdown-toc-5解析-as3-integer\">5、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Integer</code></a></li>\n <li><a href=\"#6解析-as3-boolean\" id=\"markdown-toc-6解析-as3-boolean\">6、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Boolean</code></a></li>\n <li><a href=\"#7开始引用与结束引用\" id=\"markdown-toc-7开始引用与结束引用\">7、开始引用与结束引用</a></li>\n <li><a href=\"#8解析-as3-bytearray\" id=\"markdown-toc-8解析-as3-bytearray\">8、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code></a></li>\n <li><a href=\"#9解析-as3-date\" id=\"markdown-toc-9解析-as3-date\">9、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Date</code></a></li>\n <li><a href=\"#10解析-as3-dictionary\" id=\"markdown-toc-10解析-as3-dictionary\">10、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Dictionary</code></a></li>\n </ul>\n </li>\n</ul>\n\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。</p>\n\n<h3 id=\"一amf-数据类型定义\">一、AMF 数据类型定义</h3>\n\n<h4 id=\"1数据类型\">1、数据类型</h4>\n\n<p>各种数据类型的标示都在 AMF.h 中定义为宏</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cp\">#define AMF_NUMBER 0x00 // 浮点数\n#define AMF_BOOLEAN 0x01 // 布尔型\n#define AMF_STRING 0x02 // 字符串\n#define AMF_BEGIN_OBJECT 0x03 // 对象,开始\n#define AMF_NULL 0x05 // null\n#define AMF_UNDEFINED 0x06\n#define AMF_REFERENCE 0x07\n#define AMF_MIXED_ARRAY 0x08\n#define AMF_END_OBJECT 0x09 // 对象,结束\n#define AMF_BEGIN_TYPED_OBJECT 0x10\n#define AMF_STRICT_ARRAY 0x0A\n#define AMF_DATE 0x0B // 日期\n#define AMF_LONG_STRING 0x0C // 字符串\n#define AMF_UNSUPPORTED 0x0D\n</span> \n<span class=\"cp\">#define AMF_AVMPLUS_OBJECT 0x11\n#define AMF_END 0xFF\n</span> \n<span class=\"cp\">#define AMF3_UNDEFINED 0x00\n#define AMF3_NULL 0x01\n#define AMF3_FALSE 0x02\n#define AMF3_TRUE 0x03\n#define AMF3_INTEGER 0x04\n#define AMF3_NUMBER 0x05\n#define AMF3_STRING 0x06\n#define AMF3_DATE 0x08\n#define AMF3_ARRAY 0x09\n#define AMF3_OBJECT 0x0A\n#define AMF3_BYTEARRAY 0x0C\n#define AMF3_DICTIONARY 0x11\n</span></code></pre></div></div>\n\n<p>并定义了一个枚举类表示数据类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">AMF</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"k\">enum</span> <span class=\"n\">Type</span> <span class=\"p\">{</span>\n <span class=\"n\">Null</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">,</span>\n <span class=\"n\">Boolean</span><span class=\"p\">,</span>\n <span class=\"n\">Integer</span><span class=\"p\">,</span>\n <span class=\"n\">Number</span><span class=\"p\">,</span>\n <span class=\"n\">String</span><span class=\"p\">,</span>\n <span class=\"n\">Date</span><span class=\"p\">,</span>\n <span class=\"n\">Array</span><span class=\"p\">,</span>\n <span class=\"n\">Object</span><span class=\"p\">,</span>\n <span class=\"n\">ByteArray</span><span class=\"p\">,</span>\n <span class=\"n\">Dictionary</span><span class=\"p\">,</span>\n <span class=\"n\">RawObjectContent</span><span class=\"p\">,</span>\n <span class=\"n\">End</span>\n <span class=\"p\">};</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"2undefined-type\">2、<code class=\"language-plaintext highlighter-rouge\">undefined</code> Type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">undefined</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">undefined</code> 类型标记表示。此值不会编码任何其他信息。</p>\n\n<h4 id=\"3null-type\">3、<code class=\"language-plaintext highlighter-rouge\">null</code> Type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">null</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">null</code> 类型标记表示。此值不会编码任何其他信息。</p>\n\n<h4 id=\"4false-type\">4、<code class=\"language-plaintext highlighter-rouge\">false</code> type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">false</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">false</code> 类型标记表示,用于编码布尔值 <code class=\"language-plaintext highlighter-rouge\">false</code>。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\n\n<h4 id=\"5true-type\">5、<code class=\"language-plaintext highlighter-rouge\">true</code> type</h4>\n\n<p>true 类型由 true 类型标记表示,用于编码布尔值 true。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\n\n<h4 id=\"6integer-type\">6、<code class=\"language-plaintext highlighter-rouge\">integer</code> type</h4>\n\n<p>在 AMF 3 中,整数使用可变长度的无符号 29 位整数进行序列化。ActionScript 3.0 中的整数类型 - 有符号 <code class=\"language-plaintext highlighter-rouge\">int</code> 类型和无符号 <code class=\"language-plaintext highlighter-rouge\">uint</code> 类型 - 也使用 29 位在 AVM+中表示。如果无符号整数 (<code class=\"language-plaintext highlighter-rouge\">uint</code>) 的值大于等于 229 或者如果有符号整数 (<code class=\"language-plaintext highlighter-rouge\">int</code>) 的值大于等于 228,则它将被 AVM+ 表示为 <code class=\"language-plaintext highlighter-rouge\">double</code> 类型,并使用 AMF 3 double 类型进行序列化。</p>\n\n<h4 id=\"7double-type\">7、<code class=\"language-plaintext highlighter-rouge\">double</code> type</h4>\n\n<p>AMF 3 的 <code class=\"language-plaintext highlighter-rouge\">double</code> 类型与 AMF 0 的 <code class=\"language-plaintext highlighter-rouge\">Number</code> 类型编码方式相同。此类型用于编码 ActionScript <code class=\"language-plaintext highlighter-rouge\">Number</code> 或值大于等于 228 的 ActionScript <code class=\"language-plaintext highlighter-rouge\">int</code> 或值大于等于 229 的 ActionScript <code class=\"language-plaintext highlighter-rouge\">uint</code>。编码值始终是网络字节顺序中的 8 字节 IEEE-754 双精度浮点值 (低内存中的符号位)。</p>\n\n<h4 id=\"8string-type\">8、<code class=\"language-plaintext highlighter-rouge\">String</code> type</h4>\n\n<p>ActionScript String 值使用 AMF 3 中的单个 string 类型表示 - AMF 0 中的 <code class=\"language-plaintext highlighter-rouge\">string</code> 和 <code class=\"language-plaintext highlighter-rouge\">long string</code> 类型的概念不再使用。可以使用对隐式字符串引用表中的索引将字符串作为先前发生的字符串的引用发送。字符串使用 UTF-8 编码 - 但是头可以描述字符串文本或字符串引用。空字符串永远不会作为引用发送。</p>\n\n<h4 id=\"9xmldocument-type\">9、<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> type</h4>\n\n<p>ActionScript 3.0 引入了新的 XML 类型 (参见 3.13),但是旧版的 XMLDocument 类型在语言中被保留为 <code class=\"language-plaintext highlighter-rouge\">flash.xml.XMLDocument</code>。与 AMF 0 类似,<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> 的结构需要扁平化为字符串表示以进行序列化。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。XMLDocuments 可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> 实例的引用发送。</p>\n\n<h4 id=\"10date-type\">10、<code class=\"language-plaintext highlighter-rouge\">Date</code> type</h4>\n\n<p>在 AMF 3 中,ActionScript Date 简单地作为自 1970 年 1 月 1 日午夜 (UTC 时区) 以来的毫秒数进行序列化。不发送本地时区信息。可以使用对隐式对象引用表中的索引将日期作为先前发生的日期实例的引用发送。</p>\n\n<h4 id=\"11array-type\">11、<code class=\"language-plaintext highlighter-rouge\">Array</code> type</h4>\n\n<p>ActionScript 数组的类型和在数组中的位置是基于它们的索引性质描述的。以下表格概述了这些术语的含义:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">strict</code>:仅包含序数(数字)索引</li>\n <li><code class=\"language-plaintext highlighter-rouge\">dense</code>:序数索引从 0 开始,并且在连续索引之间不存在间隙(即,从 0 到数组长度的每一个索引都被定义了)</li>\n <li><code class=\"language-plaintext highlighter-rouge\">sparse</code>:包含至少两个索引之间的一个间隙</li>\n <li><code class=\"language-plaintext highlighter-rouge\">associative</code>:包含至少一个非序数(字符串)索引(有时称为 ECMA 数组)</li>\n</ul>\n\n<p>AMF 将数组分为两部分,密集部分和关联部分。关联部分的二进制表示由名称/值对(可能没有)终止的空字符串。密集部分的二进制表示由密集部分的大小(可能为零)以及有序的值列表(可能没有)组成。在 AMF 中写入的顺序是密集部分的大小,一个以空字符串终止的名称/值对列表,然后是大小的值。数组可以通过使用隐式对象引用表的索引作为先前发生的数组的引用来发送。</p>\n\n<h4 id=\"12object-type\">12、<code class=\"language-plaintext highlighter-rouge\">Object</code> type</h4>\n\n<p>AMF 3 中有一种类型用于处理 ActionScript 对象和自定义用户类。使用术语 “traits” 来描述类的定义特征。除了 “anonymous” 对象和 “typed” 对象,ActionScript 3.0 还引入了两个进一步的 traits 来描述如何序列化对象,即 “dynamic” 和 “externalizable”。以下表格概述了这些术语和它们的含义:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">Anonymous</code>:实际的 ActionScript 对象类型的实例或没有注册别名的类的实例(在反序列化时将其视为对象)。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Typed</code>:具有注册别名的类的实例。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Dynamic</code>:具有动态特征声明的类定义的实例;可以在运行时动态地从实例中添加和删除公共变量成员。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Externalizable</code>:实现 flash.utils.IExternalizable 的类的实例,它完全控制其成员的序列化(特征信息中不包含属性名)。</li>\n</ul>\n\n<p>在这些特征之外,对象的特征信息还可能包括在类上定义的一组公共变量和公共可读写属性名称(即不是函数的公共成员)。成员名称的顺序很重要,因为在特征信息之后的成员值将按照完全相同的顺序出现。这些成员被视为密封成员,因为它们是由类型明确定义的。</p>\n\n<p>如果类型是动态的,则在密封成员之后可以包括一个进一步的部分,该部分将动态成员列为名称/值对。当遇到空字符串名称时,继续读取动态成员。</p>\n\n<p>对象可以通过使用隐式对象引用表中的索引来作为先前发生对象的引用。此外,还可以通过使用隐式特征引用表中的索引将特征信息发送为先前发生的一组特征的引用。</p>\n\n<h4 id=\"13xml-type\">13、<code class=\"language-plaintext highlighter-rouge\">XML</code> type</h4>\n\n<p>ActionScript 3.0 引入了一种新的 <code class=\"language-plaintext highlighter-rouge\">XML</code> 类型,支持 E4X 语法。为了序列化,需要将 <code class=\"language-plaintext highlighter-rouge\">XML</code> 类型展平成字符串表示形式。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。<code class=\"language-plaintext highlighter-rouge\">XML</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 XML 实例的引用发送。请注意,这种编码对 <code class=\"language-plaintext highlighter-rouge\">XML</code> 的使用造成了一些理论限制。每个 UTF-8 编码的 <code class=\"language-plaintext highlighter-rouge\">XML</code> 实例的字节长度最大为 228-1 字节(大约 256 MB)。</p>\n\n<h4 id=\"14bytearray-type\">14、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> type</h4>\n\n<p>用于保存字节数组,即 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>。AMF 3 使用可变长度编码 29 位整数序列化此类型,其中包括字节长度前缀,然后是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 的原始字节。<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实例的引用发送。</p>\n\n<h4 id=\"15amf3-的使用\">15、AMF3 的使用</h4>\n\n<h5 id=\"151netconnection-and-amf-3\">15.1、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> and AMF 3</h5>\n\n<p>除了序列化 ActionScript 类型外,AMF 还可用于远程服务的异步调用。可使用简单的消息结构将一批请求发送到远程端点。此消息结构的格式为 AMF 0(参见[AMF0])。可以使用特殊的 <code class=\"language-plaintext highlighter-rouge\">avmplus-object-marker</code> 类型将上下文头值或消息正文切换到 AMF 3 编码。</p>\n\n<h5 id=\"152netconnection-in-actionscript-30\">15.2、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> in ActionScript 3.0</h5>\n\n<p>在 ActionScript 3.0 中,NetConnection 的限定类名是 flash.net.NetConnection。这个类仍然使用响应器来处理远程端点的结果和状态响应,但是现在需要强类型的 Responder 类。完全限定的类名是 flash.net.Responder。除了正常的结果和状态响应之外,NetConnection 还会分发事件,开发人员可以添加监听器。下面是这些事件的概述:</p>\n\n<ul>\n <li>当异常异步抛出时触发,例如来自本机异步代码。</li>\n <li>当输入或输出错误导致网络操作失败时触发。</li>\n <li>当 NetConnection 对象报告其状态或错误条件时触发。</li>\n <li>如果对 NetConnection.call() 的调用尝试连接到调用者安全沙箱外的服务器,则会触发。</li>\n</ul>\n\n<h5 id=\"153bytearray-idatainput-and-idataoutput\">15.3、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code>, <code class=\"language-plaintext highlighter-rouge\">IDataInput</code> and <code class=\"language-plaintext highlighter-rouge\">IDataOutput</code></h5>\n\n<p>ActionScript 3.0 引入了一种新类型,用于支持以字节数组形式处理原始数据,即 <code class=\"language-plaintext highlighter-rouge\">flash.utils.ByteArray</code>。为了协助 ActionScript 对象序列化和复制,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实现了 <code class=\"language-plaintext highlighter-rouge\">flash.utils.IDataInput</code> 和 <code class=\"language-plaintext highlighter-rouge\">flash.utils.IDataOutput</code>。这些接口指定了帮助将常见类型写入字节流的实用方法。两个感兴趣的方法是 <code class=\"language-plaintext highlighter-rouge\">IDataOutput.writeObject</code> 和 <code class=\"language-plaintext highlighter-rouge\">IDataInput.readObject</code>。这些方法使用 AMF 编码对象。使用的 AMF 版本由 <code class=\"language-plaintext highlighter-rouge\">ByteArray.objectEncoding</code> 方法控制,该方法可以设置为 AMF 3 或 AMF 0。枚举类型 <code class=\"language-plaintext highlighter-rouge\">flash.net.ObjectEncoding</code> 包含 AMF 版本的常量:分别为 <code class=\"language-plaintext highlighter-rouge\">ObjectEncoding.AMF0</code> 和 <code class=\"language-plaintext highlighter-rouge\">ObjectEncoding.AMF3</code>。</p>\n\n<p>请注意,<code class=\"language-plaintext highlighter-rouge\">ByteArray.writeObject</code> 使用一个版本的 AMF 对整个对象进行编码。与 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 不同,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 不会从 AMF 0 开始,然后将 <code class=\"language-plaintext highlighter-rouge\">objectEncoding</code> 属性设置为 AMF 3 并切换到 AMF 3。还请注意,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 为每个 <code class=\"language-plaintext highlighter-rouge\">readObject</code> 和 <code class=\"language-plaintext highlighter-rouge\">writeObject</code> 调用使用新的对象、对象特征和字符串的隐式引用表。</p>\n\n<h3 id=\"二binaryreaderwriter\">二、<code class=\"language-plaintext highlighter-rouge\">BinaryReader/Writer</code></h3>\n\n<h4 id=\"1amf3-数据格式基础\">1、AMF3 数据格式基础</h4>\n\n<p>首先介绍一下变长整数(Variable Length Integer),比如 UInt32 如下。</p>\n\n<p><img src=\"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png\" alt=\"image\" /></p>\n\n<p>上图摘自 Adobe AMF3 官方文档,这是一种压缩方式的整数存储,且每一字节都对后面的数据具有预知作用。那么字符串如何处理呢?下面是字符串的处理方式,AMF0 和 AMF3 都才用 UTF-8 编码方式,并做如下压缩处理:</p>\n\n<p><img src=\"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png\" alt=\"image\" /></p>\n\n<p>上图摘自 Adobe AMF3 官方文档。</p>\n\n<h4 id=\"2序列化\">2、序列化</h4>\n\n<p>序列化包括 8 位、16 位、32 位,以及 UTF-8 和 UTF-16(I guess)编码的 String,还有原生数据(Raw Data)、变长无符号整数(Variable Length Unsigned Integer)以及 IP 地址。所谓序列化就是按照指定格式编写各种对象、基础数据类型值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">BinaryWriter</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ostream</span><span class=\"o\">&</span> <span class=\"n\">ostr</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">BinaryWriter</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write7BitValue</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write7BitLongValue</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt64</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeAddress</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Address</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">,</span><span class=\"kt\">bool</span> <span class=\"n\">publicFlag</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeAddress</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Net</span><span class=\"o\">::</span><span class=\"n\">SocketAddress</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">,</span><span class=\"kt\">bool</span> <span class=\"n\">publicFlag</span><span class=\"p\">);</span>\n <span class=\"k\">static</span> <span class=\"n\">BinaryWriter</span> <span class=\"n\">BinaryWriterNull</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>请注意其中名为 <code class=\"language-plaintext highlighter-rouge\">BinaryWriterNull</code> 的成员。构造函数定义为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">ostream</span><span class=\"o\">&</span> <span class=\"n\">ostr</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">ostr</span><span class=\"p\">,</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">NETWORK_BYTE_ORDER</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n\n<span class=\"n\">BinaryWriter</span><span class=\"o\">::~</span><span class=\"n\">BinaryWriter</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">writeRaw</code> 是简单地封装 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter::writeRaw()</code>,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入整数实现如下,用的是从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 继承来的重载运算符操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span> \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入字符串:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入变长整数,这段代码含义也一目了然,就是读取变长无符号 32 位整数、64 位整数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write7BitValue</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">Get7BitValueSize</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span><span class=\"o\">*</span><span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">21</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"c1\">// 4 bytes maximum</span>\n <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"mi\">22</span><span class=\"p\">;</span>\n <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">7</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"mh\">0x80</span> <span class=\"o\">|</span> <span class=\"p\">((</span><span class=\"n\">value</span><span class=\"o\">>></span><span class=\"n\">shift</span><span class=\"p\">)</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">));</span>\n <span class=\"n\">shift</span> <span class=\"o\">-=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">max</span> <span class=\"o\">?</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0xFF</span> <span class=\"o\">:</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write7BitLongValue</span><span class=\"p\">(</span><span class=\"n\">UInt64</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">Get7BitValueSize</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span><span class=\"o\">*</span><span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">63</span><span class=\"p\">;</span> <span class=\"c1\">// Can give 10 bytes!</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">max</span><span class=\"p\">)</span>\n <span class=\"o\">++</span><span class=\"n\">shift</span><span class=\"p\">;</span>\n \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">7</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"mh\">0x80</span> <span class=\"o\">|</span> <span class=\"p\">((</span><span class=\"n\">value</span><span class=\"o\">>></span><span class=\"n\">shift</span><span class=\"p\">)</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">));</span>\n <span class=\"n\">shift</span> <span class=\"o\">-=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">max</span> <span class=\"o\">?</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0xFF</span> <span class=\"o\">:</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入 IP 地址的两个函数暂略。</p>\n\n<h4 id=\"3反序列化\">3、反序列化</h4>\n\n<p>反序列化就是从指定格式的数据中读出各类型的数据值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">BinaryReader</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">istream</span><span class=\"o\">&</span> <span class=\"n\">istr</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">BinaryReader</span><span class=\"p\">();</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt64</span> <span class=\"n\">read7BitLongValue</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read7BitEncoded</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString8</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString16</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">read16</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read32</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readAddress</span><span class=\"p\">(</span><span class=\"n\">Address</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">);</span>\n \n <span class=\"k\">static</span> <span class=\"n\">BinaryReader</span> <span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>构造与析构函数都很简单:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">istream</span><span class=\"o\">&</span> <span class=\"n\">istr</span><span class=\"p\">)</span> <span class=\"o\">:</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">istr</span><span class=\"p\">,</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">NETWORK_BYTE_ORDER</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">BinaryReader</span><span class=\"o\">::~</span><span class=\"n\">BinaryReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读取原生数据(Raw Data):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写整数,用的是 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter</code> 的重载运算符:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读写整数依旧使用从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 继承来的运算符操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt8</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read8</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">UInt16</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read16</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">UInt32</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read32</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写字符串:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读取变长整数,分别针对 <code class=\"language-plaintext highlighter-rouge\">UInt32</code> 和 <code class=\"language-plaintext highlighter-rouge\">UInt64</code>,要理解 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 的变长整数才能理解这个:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x80</span><span class=\"p\">)</span> <span class=\"o\">&&</span> <span class=\"n\">n</span> <span class=\"o\"><</span> <span class=\"mi\">3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"p\">(</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"o\">++</span><span class=\"n\">n</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"p\">((</span><span class=\"n\">n</span><span class=\"o\"><</span><span class=\"mi\">3</span><span class=\"p\">)</span> <span class=\"o\">?</span> <span class=\"mi\">7</span> <span class=\"o\">:</span> <span class=\"mi\">8</span><span class=\"p\">);</span> <span class=\"c1\">// Use all 8 bits from the 4th byte</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"n\">b</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt64</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read7BitLongValue</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">UInt64</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x80</span><span class=\"p\">)</span> <span class=\"o\">&&</span> <span class=\"n\">n</span> <span class=\"o\"><</span> <span class=\"mi\">8</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"p\">(</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"o\">++</span><span class=\"n\">n</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"p\">((</span><span class=\"n\">n</span><span class=\"o\"><</span><span class=\"mi\">8</span><span class=\"p\">)</span> <span class=\"o\">?</span> <span class=\"mi\">7</span> <span class=\"o\">:</span> <span class=\"mi\">8</span><span class=\"p\">);</span> <span class=\"c1\">// Use all 8 bits from the 4th byte</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"n\">b</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三packetreaderwriter\">三、<code class=\"language-plaintext highlighter-rouge\">PacketReader/Writer</code></h3>\n\n<h4 id=\"1packetreader\">1、PacketReader</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#define PACKETRECV_SIZE 2048\nclass PacketReader: public BinaryReader {\npublic:\n PacketReader(const Poco::UInt8* buffer,Poco::UInt32 size);\n PacketReader(PacketReader&);\n virtual ~PacketReader();\n const Poco::UInt32 fragments;\n Poco::UInt32 available(); // 可读字节数\n Poco::UInt8* current();\n Poco::UInt32 position(); // 获取当前的相对位置(相对于起始位置的)\n void reset(Poco::UInt32 newPos = 0); // 设定当前位置\n void shrink(Poco::UInt32 rest);\n void next(Poco::UInt32 size);\nprivate:\n MemoryInputStream _memory;\n};\n</code></pre></div></div>\n\n<h6 id=\"11封装-memoryinputstream\">1.1、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">current</code>:当前绝对位置(内存地址)</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code>:当前位置(绝对位置)减去缓冲区起始位置</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"12收缩缓冲区\">1.2、收缩缓冲区</h6>\n\n<p>封装了 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 的 <code class=\"language-plaintext highlighter-rouge\">resize</code>。不过由于前面的 <code class=\"language-plaintext highlighter-rouge\">if</code> 语句影响,传给 resize 的参数一定不会大于缓冲区的当前大小。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">shrink</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">rest</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">rest</span> <span class=\"o\">></span> <span class=\"n\">available</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"rest %u more upper than available %u bytes\"</span><span class=\"p\">,</span><span class=\"n\">rest</span><span class=\"p\">,</span><span class=\"n\">available</span><span class=\"p\">());</span>\n <span class=\"n\">rest</span> <span class=\"o\">=</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">rest</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"13构造函数拷贝构造函数和析构函数\">1.3、构造函数、拷贝构造函数和析构函数</h6>\n\n<p>构造函数先调用父类 <code class=\"language-plaintext highlighter-rouge\">BinaryReader</code> 的构造函数,并初始化 <code class=\"language-plaintext highlighter-rouge\">fragments</code> 和 <code class=\"language-plaintext highlighter-rouge\">_memory</code> 输入流的缓冲区。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">PacketReader</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">fragments</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"c1\">// Consctruction by copy</span>\n<span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">PacketReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">fragments</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">fragments</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">PacketReader</span><span class=\"o\">::~</span><span class=\"n\">PacketReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2packetwriter\">2、<code class=\"language-plaintext highlighter-rouge\">PacketWriter</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">PacketWriter</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">BinaryWriter</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"n\">PacketWriter</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">PacketWriter</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">good</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">clear</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">limit</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"n\">MemoryOutputStream</span> <span class=\"n\">_memory</span><span class=\"p\">;</span>\n <span class=\"n\">PacketWriter</span><span class=\"o\">*</span> <span class=\"n\">_pOther</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h6 id=\"21封装memoryoutputstream\">2.1、封装<code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">good</code>:不过 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code> 也是封装的 <code class=\"language-plaintext highlighter-rouge\">std::ostream</code> 的 <code class=\"language-plaintext highlighter-rouge\">good</code> 函数,True if no error flags are set.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">bool</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">good</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">good</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">written</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">length</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code>:设置缓冲区的指针位置,即 <code class=\"language-plaintext highlighter-rouge\">position</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code>:移动缓冲区指针</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin</code>:返回缓冲区的起始地址</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">clear</code>:其实就是修改 written 和 position,使得指定位置后面的数据在以后写的时候可以被覆盖,并不是真正的清除。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">clear</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">limit</code>:封装 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code> 的 <code class=\"language-plaintext highlighter-rouge\">resize</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">limit</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">length</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">length</span> <span class=\"o\">=</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">length</span> <span class=\"o\">></span> <span class=\"n\">_size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"Limit '%d' more upper than buffer size '%d' bytes\"</span><span class=\"p\">,</span><span class=\"n\">length</span><span class=\"p\">,</span><span class=\"n\">_size</span><span class=\"p\">);</span>\n <span class=\"n\">length</span> <span class=\"o\">=</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">length</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"22封装-binarywriter\">2.2、封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">flush</code>:封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code> 的 <code class=\"language-plaintext highlighter-rouge\">flush</code>,不过 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code> 的 <code class=\"language-plaintext highlighter-rouge\">flush</code> 实际上是从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter</code> 继承而来的。其作用是「Flushes the underlying stream」。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">flush</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_pOther</span> <span class=\"o\">&&</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"n\">_pOther</span><span class=\"o\">-></span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">())</span>\n <span class=\"n\">_pOther</span><span class=\"o\">-></span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">());</span>\n <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">flush</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"23构造函数拷贝构造函数和析构函数\">2.3、构造函数、拷贝构造函数和析构函数</h6>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">_pOther</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_size</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"c1\">// Consctruction by copy</span>\n<span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"n\">PacketWriter</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_pOther</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">other</span><span class=\"p\">),</span>\n <span class=\"n\">_memory</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">_size</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>注意析构函数中会进行 <code class=\"language-plaintext highlighter-rouge\">flush</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketWriter</span><span class=\"o\">::~</span><span class=\"n\">PacketWriter</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"四amfreader\">四、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code></h3>\n\n<h4 id=\"1objectdef\">1、<code class=\"language-plaintext highlighter-rouge\">ObjectDef</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ObjectDef</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span> \n <span class=\"n\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">amf3</span><span class=\"p\">(</span><span class=\"n\">amf3</span><span class=\"p\">),</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">dynamic</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">externalizable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">count</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">arrayType</span><span class=\"p\">(</span><span class=\"n\">arrayType</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">string</span><span class=\"o\">></span> <span class=\"n\">hardProperties</span><span class=\"p\">;</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">reset</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">dynamic</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">externalizable</span><span class=\"p\">;</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">count</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"p\">;</span>\n <span class=\"k\">const</span> <span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"2amfreader-定义\">2、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 定义</h4>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 作为其成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">AMFReader</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">AMFReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">AMFReader</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">readSimpleObject</span><span class=\"p\">(</span><span class=\"n\">AMFSimpleObject</span><span class=\"o\">&</span> <span class=\"n\">object</span><span class=\"p\">);</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">read</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">double</span> <span class=\"n\">readNumber</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Int32</span> <span class=\"n\">readInteger</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readBoolean</span><span class=\"p\">();</span>\n <span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">readByteArray</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">&</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Timestamp</span> <span class=\"n\">readDate</span><span class=\"p\">();</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">readObject</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readArray</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readDictionary</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">weakKeys</span><span class=\"p\">);</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readKey</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readValue</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readItem</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">);</span>\n <span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">readRawObjectContent</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">readNull</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">startReferencing</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">stopReferencing</span><span class=\"p\">();</span>\n \n <span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">;</span>\n \n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">ObjectDef</span><span class=\"o\">*></span> <span class=\"n\">_objectDefs</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_stringReferences</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_classDefReferences</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_amf0References</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_amf0Reset</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_reset</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_amf3</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">_referencing</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"21构造函数析构函数\">2.1、构造函数、析构函数</h5>\n\n<p>参数为 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code>,会初始化一些成员变量。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">AMFReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">reader</span><span class=\"p\">(</span><span class=\"n\">reader</span><span class=\"p\">),</span>\n <span class=\"n\">_reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_amf3</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_amf0Reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_referencing</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构时,会逐一释放 <code class=\"language-plaintext highlighter-rouge\">_objectDefs</code> 中对象的内存:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMFReader</span><span class=\"o\">::~</span><span class=\"n\">AMFReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">ObjectDef</span><span class=\"o\">*>::</span><span class=\"n\">iterator</span> <span class=\"n\">it</span><span class=\"p\">;</span>\n <span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span>\n <span class=\"k\">delete</span> <span class=\"o\">*</span><span class=\"n\">it</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22简单封装-packetreader-的一些函数\">2.2、简单封装 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 的一些函数</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code>:操作指针位置</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_reset</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"p\">);</span>\n <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code>:根据当前缓冲区大小和 <code class=\"language-plaintext highlighter-rouge\">written</code> 计算得到</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">current</code>:<code class=\"language-plaintext highlighter-rouge\">gptr</code> 内存地址</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">*</span><span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23设置-gptr-位置\">2.3、设置 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 位置</h5>\n\n<p>其实 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 也被影响了,但是在 <code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 中只用 <code class=\"language-plaintext highlighter-rouge\">gptr</code>。调用构造函数的时候,<code class=\"language-plaintext highlighter-rouge\">reset</code> 被设为 0,其后在每次读取数据的时候都会影响 <code class=\"language-plaintext highlighter-rouge\">reset</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"p\">);</span>\n <span class=\"n\">_reset</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"24判断类型\">2.4、判断类型</h5>\n\n<p>分析请看注释:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">followingType</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先 <code class=\"language-plaintext highlighter-rouge\">reset</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span> <span class=\"o\">!=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">back</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">amf3</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>是 AMF0 类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>如果没有可读数据了,则返回 AMF::End。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">available</span><span class=\"p\">())</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">End</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>开始读了,先读到的表示 AMF 数据类型。要注意的是调用 current 并不改变指针的位置,所以你会在线面看到调用 next。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt8</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_amf3</span> <span class=\"o\">&&</span> <span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF_AVMPLUS_OBJECT</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">available</span><span class=\"p\">())</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">End</span><span class=\"p\">;</span>\n <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>AMF3 类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">switch</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>Undefined 和 null 都当做 null。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF3_UNDEFINED</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_NULL</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>false 和 true 都是 boolean。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF3_FALSE</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_TRUE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_INTEGER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Integer</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_NUMBER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">String</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_DATE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Array</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_DICTIONARY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Dictionary</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Object</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_BYTEARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">ByteArray</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>落到 default 手里的话,就跳过这个字节,读取下一个。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"nl\">default:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Unknown AMF3 type %.2x\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>AMF0 类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">switch</span> <span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">undefined</code> 和 <code class=\"language-plaintext highlighter-rouge\">null</code> 都是 <code class=\"language-plaintext highlighter-rouge\">null</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_UNDEFINED</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_NULL</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">;</span>\n \n <span class=\"k\">case</span> <span class=\"n\">AMF_BOOLEAN</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_NUMBER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">long string</code> 和 <code class=\"language-plaintext highlighter-rouge\">string</code> 都是 <code class=\"language-plaintext highlighter-rouge\">string</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_LONG_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">String</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">mixed array</code> 和 <code class=\"language-plaintext highlighter-rouge\">strict array</code> 都是 <code class=\"language-plaintext highlighter-rouge\">array</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_MIXED_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_STRICT_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Array</span><span class=\"p\">;</span>\n \n <span class=\"k\">case</span> <span class=\"n\">AMF_DATE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin object</code> 和 <code class=\"language-plaintext highlighter-rouge\">begin typed object</code> 都是 <code class=\"language-plaintext highlighter-rouge\">object</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_BEGIN_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_BEGIN_TYPED_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Object</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是引用,就跳过表示类型值的这个字节。这个先留下来,带我们分析完 readArray 和 readObject 再回头看。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_REFERENCE</span><span class=\"p\">:</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read16</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">reference</span> <span class=\"o\">></span> <span class=\"n\">_amf0References</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF0 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_amf0Reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_amf0References</span><span class=\"p\">[</span><span class=\"n\">reference</span><span class=\"p\">]);</span>\n <span class=\"k\">return</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果没了,或者不支持,或者都不是,就跳过这个字节,递归继续读取:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_END_OBJECT</span><span class=\"p\">:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF end object type without begin object type before\"</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_UNSUPPORTED</span><span class=\"p\">:</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"Unsupported type in AMF format\"</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">default</span><span class=\"o\">:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Unknown AMF type %.2x\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">followingType</code> 是这个类的核心,每个具体的数据类型的分析都依赖于它的判断。这些类型的解析,会在下一篇文章中介绍。</p>\n\n<h4 id=\"3解析-as3-null\">3、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Null</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readNull</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先 reset 一下是惯例,就像糗百上的割一样。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span> \n</code></pre></div></div>\n\n<p>AMF 数据类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">Null</code>,跳过该字节,并返回</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>判断错误</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Null type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4解析-as3-number\">4、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Number</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">double</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readNumber</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Null</code> 会被悲催的跳过:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">Number</code> 呀 :(</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Number type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过该字节吧</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>返回吧,返回之前还用到 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 的运算符,注意 AS3 中的 <code class=\"language-plaintext highlighter-rouge\">Number</code> 就是 C++ 的 <code class=\"language-plaintext highlighter-rouge\">double</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">double</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5解析-as3-integer\">5、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Integer</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Int32</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readInteger</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code> 类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>Null 的话:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">Integer</code> 或者 Number 的话。。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Integer</span> <span class=\"o\">&&</span> <span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Integer type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过吧。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>终于是 <code class=\"language-plaintext highlighter-rouge\">Number</code> 了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kt\">double</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Int32</span><span class=\"p\">)</span><span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读一个变长的 32 位无符号整数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Forced in AMF3 here!</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">value</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果大于 3.5 个字节所能表示的最大无符号整数值(<code class=\"language-plaintext highlighter-rouge\">268435455</code> 是 <code class=\"language-plaintext highlighter-rouge\">0xFFFFFFF</code>),则减去 <code class=\"language-plaintext highlighter-rouge\">0x2FFFFFFF</code>(这还不是太理解,有能解释的朋友给留个言,或者发 email 给我 )</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">></span> <span class=\"mi\">268435455</span><span class=\"p\">)</span>\n <span class=\"n\">value</span> <span class=\"o\">-=</span> <span class=\"p\">(</span><span class=\"mi\">1</span> <span class=\"o\"><<</span> <span class=\"mi\">29</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"6解析-as3-boolean\">6、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Boolean</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readBoolean</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">Null</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>居然不是 <code class=\"language-plaintext highlighter-rouge\">Boolean</code> 的话。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Boolean type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 的话,返回 <code class=\"language-plaintext highlighter-rouge\">true</code> 或者 <code class=\"language-plaintext highlighter-rouge\">false</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span><span class=\"o\">==</span> <span class=\"n\">AMF3_FALSE</span> <span class=\"o\">?</span> <span class=\"nb\">false</span> <span class=\"o\">:</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 就是 <code class=\"language-plaintext highlighter-rouge\">AMF0</code> 喽:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span><span class=\"o\">==</span><span class=\"mh\">0x00</span> <span class=\"o\">?</span> <span class=\"nb\">false</span> <span class=\"o\">:</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"7开始引用与结束引用\">7、开始引用与结束引用</h4>\n\n<p>如下这两个函数会在 <code class=\"language-plaintext highlighter-rouge\">FlowConnection</code> 中调用。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">startReferencing</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_referencing</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">stopReferencing</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_referencing</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"8解析-as3-bytearray\">8、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code></h4>\n\n<p>先回顾一下 AMF3 中的ByteArray 的数据格式:</p>\n\n<p>注意到,首先要读取一个变长无符号 32 位整数,但是最低位是 1,只有 28 位用于表示数据长度。解释完这里,下面的解析过程才好理解。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readByteArray</span><span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Null</code> 就返回 <code class=\"language-plaintext highlighter-rouge\">BinaryReaderNull</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>,也返回 <code class=\"language-plaintext highlighter-rouge\">BinaryReaderNull</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">ByteArray</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF ByteArray type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过这个字节:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>注意 position 返回的是相对位置,与 AS3 中一样。<code class=\"language-plaintext highlighter-rouge\">reference</code> 表示这个地址(简单说,引用就是地址嘛)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>读取一个变长 32 位无符号整数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>最低位是 1 的话,<code class=\"language-plaintext highlighter-rouge\">isInline</code> 是 <code class=\"language-plaintext highlighter-rouge\">true</code>,否则为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">size</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>右移一位,因为那一位是标志位,上面解释过了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">isInline</code> 是 <code class=\"language-plaintext highlighter-rouge\">true</code>,表示是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">_referencing</code> 为 <code class=\"language-plaintext highlighter-rouge\">true</code> 的话(这是一个 <code class=\"language-plaintext highlighter-rouge\">vector</code>),push back this reference:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>不符合 ByteArray 的格式定义的话:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">size</span> <span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>移动到这个 reference 的位置,<code class=\"language-plaintext highlighter-rouge\">_references[size]</code> 就是这个位置(相对)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">size</span><span class=\"p\">]);</span> <span class=\"c1\">// TODO size 作为索引,还没有完全理解</span>\n</code></pre></div></div>\n\n<p>读取这个 reference 的 size 值给 size对象(注意 size 是这个函数传入的引用参数,其值可以被修改)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"o\">>></span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>把读取完 ByteArraty 的 PacketReader 返回:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>最后强调一点,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 的数据段最大长度为 228 -1 字节,约为 256 MB。</p>\n\n<h4 id=\"9解析-as3-date\">9、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Date</code></h4>\n\n<p>先看下 <code class=\"language-plaintext highlighter-rouge\">Date</code> 的数据格式:</p>\n\n<p>下面开始分析:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Timestamp</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readDate</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>Null 的话,就返回当前时间:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是 Date 类型,也返回当前时间:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Date type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"kt\">double</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是 AMF3:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先读取 flag,最低一位必须是 1,其他位丢到垃圾桶。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">flags</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>当前相对位置。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>是 1 就 push back 到 <code class=\"language-plaintext highlighter-rouge\">_references</code> 里。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">flags</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>读取一个 double,到 result 里(result 也是 double 类型哦~)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>如果标志位不是 1,麻烦不少哒。。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">flags</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果 flag 超了,就返回当前时间作为时间戳作为 Date。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">flags</span> <span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这段与 ByteArray 那段一样:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">flags</span><span class=\"p\">]);</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>返回喽~</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"nf\">Timestamp</span><span class=\"p\">((</span><span class=\"n\">Timestamp</span><span class=\"o\">::</span><span class=\"n\">TimeVal</span><span class=\"p\">)</span> <span class=\"n\">result</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>读俩,因为是 double(64 位):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">2</span><span class=\"p\">);</span> <span class=\"c1\">// Timezone, useless</span>\n</code></pre></div></div>\n\n<p>返回喽~</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"nf\">Timestamp</span><span class=\"p\">((</span><span class=\"n\">Timestamp</span><span class=\"o\">::</span><span class=\"n\">TimeVal</span><span class=\"p\">)</span> <span class=\"n\">result</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"10解析-as3-dictionary\">10、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Dictionary</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readDictionary</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">weakKeys</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>下面这段咱就略了。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Dictionary</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Dictionary type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过 type:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// AMF3</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span> <span class=\"c1\">// marker</span>\n</code></pre></div></div>\n\n<p>当前相对位置值作为 <code class=\"language-plaintext highlighter-rouge\">reference</code>,再读个 <code class=\"language-plaintext highlighter-rouge\">size</code>,还是最低位必须为 1,不是就返回 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">size</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n <span class=\"n\">size</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">isInline</span> <span class=\"o\">&&</span> <span class=\"n\">size</span><span class=\"o\">></span><span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>下面要调用到 <code class=\"language-plaintext highlighter-rouge\">ObjectRef</code> 构造函数,这里再把其实现拿出来看看,其实主要是初始化了哪些成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">amf3</span><span class=\"p\">(</span><span class=\"n\">amf3</span><span class=\"p\">),</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">dynamic</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">externalizable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">count</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">arrayType</span><span class=\"p\">(</span><span class=\"n\">arrayType</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>可以看到要有一个 amf3,还有 <code class=\"language-plaintext highlighter-rouge\">reset</code> 置为 0,<code class=\"language-plaintext highlighter-rouge\">dynamic</code> 置为 <code class=\"language-plaintext highlighter-rouge\">false</code>,<code class=\"language-plaintext highlighter-rouge\">externalizable</code> 也是 <code class=\"language-plaintext highlighter-rouge\">false</code>,<code class=\"language-plaintext highlighter-rouge\">count</code> 是 0,<code class=\"language-plaintext highlighter-rouge\">arrayType</code> 成员要赋值。</p>\n\n<p>上面是插播哦,下面还要继续哒。创建这么一个对象,注意是 new 出来的,所以我们在《OpenRTMFP/Cumulus Primer(16)AMF解析之AMFReader》一文中提到了 AMFReader 的析构函数中要对 <code class=\"language-plaintext highlighter-rouge\">_objectRef</code> 的每个元素逐一析构的。<code class=\"language-plaintext highlighter-rouge\">arrayType</code> 就设置为 <code class=\"language-plaintext highlighter-rouge\">AMF3_DICTIONARY</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ObjectDef</span><span class=\"o\">*</span> <span class=\"n\">pObjectDef</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nf\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">,</span> <span class=\"n\">AMF3_DICTIONARY</span><span class=\"p\">);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">dynamic</span><span class=\"o\">=</span><span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pObjectDef</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果标志位是 1,就直接 push back,跟之前一样。不过这里多了一个 <code class=\"language-plaintext highlighter-rouge\">pObjectDef</code>,所以还要设置一下它的计数为 <code class=\"language-plaintext highlighter-rouge\">size</code>,就是 <code class=\"language-plaintext highlighter-rouge\">dictionary</code> 数据大小。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">=</span> <span class=\"n\">size</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果标志位是 0,就把 <code class=\"language-plaintext highlighter-rouge\">count</code> 设置为下一个变长整数值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">size</span><span class=\"p\">]);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"o\">>></span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">*=</span> <span class=\"mi\">2</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>读一个字节,如果最小位是 1,weakKeys 就是 true,否则为 false。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">weakKeys</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n \n <span class=\"k\">return</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</title>\n \t<meta name=\"description\" content=\"CumulusServer 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 CumulusEdge 和 CumulusServer 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 CumulusServer 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</h2>\t\t\n\t<time datetime=\"2012-04-15T14:26:58+00:00\" class=\"by-line\">15 Apr 2012, 广州 | 作者 麦克船长 | 总计 3844 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#1绑定地址\" id=\"markdown-toc-1绑定地址\">1、绑定地址</a></li>\n <li><a href=\"#2cumulusserver-接收数据\" id=\"markdown-toc-2cumulusserver-接收数据\">2、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 接收数据</a></li>\n <li><a href=\"#3如果-cumulusedge-端口存在且-edge-socket-可用\" id=\"markdown-toc-3如果-cumulusedge-端口存在且-edge-socket-可用\">3、如果 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 端口存在且 edge socket 可用。</a></li>\n <li><a href=\"#4cumulusserver-和-cumulusedge-的-socket-都没有数据\" id=\"markdown-toc-4cumulusserver-和-cumulusedge-的-socket-都没有数据\">4、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 都没有数据:</a></li>\n <li><a href=\"#5发送方的-ip-被禁\" id=\"markdown-toc-5发送方的-ip-被禁\">5、发送方的 ip 被禁:</a></li>\n <li><a href=\"#6数据包长度小于可能的最小值12\" id=\"markdown-toc-6数据包长度小于可能的最小值12\">6、数据包长度小于可能的最小值(12)</a></li>\n</ul>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。</p>\n\n<p>本所要介绍的这个主循环在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(const volatile bool& terminate)</code> 函数中。RTMFPServer覆盖 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的 <code class=\"language-plaintext highlighter-rouge\">run(const volatile bool &terminate)</code> 方法。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<h3 id=\"1绑定地址\">1、绑定地址</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 IP 地址和端口:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"nf\">address</span><span class=\"p\">(</span><span class=\"s\">\"0.0.0.0\"</span><span class=\"p\">,</span><span class=\"n\">_port</span><span class=\"p\">);</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">address</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n<span class=\"err\">绑定</span><span class=\"n\">CumulusEdge</span><span class=\"err\">的</span> <span class=\"n\">IP</span> <span class=\"err\">地址和端口:</span>\n\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"nf\">edgesAddress</span><span class=\"p\">(</span><span class=\"s\">\"0.0.0.0\"</span><span class=\"p\">,</span><span class=\"n\">_edgesPort</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">edgesAddress</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>发送者(Client)的 IP 地址和端口:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"n\">sender</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">buff</span><span class=\"p\">[</span><span class=\"n\">PACKETRECV_SIZE</span><span class=\"p\">];</span>\n <span class=\"kt\">int</span> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n \n <span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">stop</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">idle</span> <span class=\"o\">=</span> <span class=\"n\">realTime</span><span class=\"p\">(</span><span class=\"n\">stop</span><span class=\"p\">);</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">stop</span><span class=\"p\">)</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n \n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h3 id=\"2cumulusserver-接收数据\">2、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 接收数据</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 有数据可读:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>从 <code class=\"language-plaintext highlighter-rouge\">socket</code> 读取:把数据存到 <code class=\"language-plaintext highlighter-rouge\">buff</code>,把发送者地址赋给 <code class=\"language-plaintext highlighter-rouge\">sender</code>,把所读长度返回给 <code class=\"language-plaintext highlighter-rouge\">size</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">receiveFrom</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span><span class=\"k\">sizeof</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">),</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>处理 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 产生的异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Main socket reception : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">address</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"3如果-cumulusedge-端口存在且-edge-socket-可用\">3、如果 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 端口存在且 edge socket 可用。</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 有数据可读:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span> <span class=\"o\">></span> <span class=\"mi\">0</span> <span class=\"o\">&&</span> <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">receiveFrom</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span> <span class=\"k\">sizeof</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">),</span> <span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Main socket reception : %s\"</span><span class=\"p\">,</span> <span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">edgesAddress</span><span class=\"p\">,</span> <span class=\"nb\">true</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">Edge</span><span class=\"o\">*</span> <span class=\"n\">pEdge</span> <span class=\"o\">=</span> <span class=\"n\">edges</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pEdge</span><span class=\"p\">)</span>\n <span class=\"n\">pEdge</span><span class=\"o\">-></span><span class=\"n\">update</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<h3 id=\"4cumulusserver-和-cumulusedge-的-socket-都没有数据\">4、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 都没有数据:</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 空闲:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">idle</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>主线程等待一秒。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_timeLastManage</span><span class=\"p\">.</span><span class=\"n\">isElapsed</span><span class=\"p\">(</span><span class=\"n\">_freqManage</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>Just middle session</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_middle</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Sessions</span><span class=\"o\">::</span><span class=\"n\">Iterator</span> <span class=\"n\">it</span><span class=\"p\">;</span>\n <span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span> <span class=\"o\">!=</span> <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Middle</span><span class=\"o\">*</span> <span class=\"n\">pMiddle</span> <span class=\"o\">=</span> <span class=\"k\">dynamic_cast</span><span class=\"o\"><</span><span class=\"n\">Middle</span><span class=\"o\">*></span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMiddle</span><span class=\"p\">)</span>\n <span class=\"n\">pMiddle</span><span class=\"o\">-></span><span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_timeLastManage</span><span class=\"p\">.</span><span class=\"n\">update</span><span class=\"p\">();</span>\n <span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"5发送方的-ip-被禁\">5、发送方的 ip 被禁:</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isBanned</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">.</span><span class=\"n\">host</span><span class=\"p\">()))</span> <span class=\"p\">{</span>\n <span class=\"n\">INFO</span><span class=\"p\">(</span><span class=\"s\">\"Data rejected because client %s is banned\"</span><span class=\"p\">,</span>\n <span class=\"n\">sender</span><span class=\"p\">.</span><span class=\"n\">host</span><span class=\"p\">().</span><span class=\"n\">toString</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"6数据包长度小于可能的最小值12\">6、数据包长度小于可能的最小值(12)</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">size</span> <span class=\"o\"><</span> <span class=\"n\">RTMFP_MIN_PACKET_SIZE</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Invalid packet\"</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">PacketReader</span> <span class=\"nf\">packet</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Session</span><span class=\"o\">*</span> <span class=\"n\">pSession</span> <span class=\"o\">=</span> <span class=\"n\">findSession</span><span class=\"p\">(</span><span class=\"n\">RTMFP</span><span class=\"o\">::</span><span class=\"n\">Unpack</span><span class=\"p\">(</span><span class=\"n\">packet</span><span class=\"p\">));</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">pSession</span><span class=\"p\">)</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">checked</span><span class=\"p\">)</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">commitCookie</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pSession</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>给 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 或者自己(<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>)的 <code class=\"language-plaintext highlighter-rouge\">socket</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">setEndPoint</span><span class=\"p\">(</span><span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span> <span class=\"o\">?</span> <span class=\"n\">_edgesSocket</span> <span class=\"o\">:</span> <span class=\"n\">_socket</span><span class=\"p\">,</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">receive</span><span class=\"p\">(</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_pCirrus</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">delete</span> <span class=\"n\">_pCirrus</span><span class=\"p\">;</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"nb\">NULL</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的第二篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</h2>\t\t\n\t<time datetime=\"2012-04-14T11:20:46+00:00\" class=\"by-line\">14 Apr 2012, 广州 | 作者 麦克船长 | 总计 18890 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一cumulus-启动源码分析\" id=\"markdown-toc-一cumulus-启动源码分析\">一、Cumulus 启动源码分析</a> <ul>\n <li><a href=\"#1maincpp-中的-main-函数\" id=\"markdown-toc-1maincpp-中的-main-函数\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数</a></li>\n <li><a href=\"#2maincpp-中的-cumulusserver-构造函数\" id=\"markdown-toc-2maincpp-中的-cumulusserver-构造函数\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer()</code> 构造函数</a></li>\n <li><a href=\"#3maincpp-中的-cumulusserver-的-initialize-成员函数\" id=\"markdown-toc-3maincpp-中的-cumulusserver-的-initialize-成员函数\">3、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 成员函数</a></li>\n <li><a href=\"#4maincpp-中的-cumulusserver-的-main-成员函数\" id=\"markdown-toc-4maincpp-中的-cumulusserver-的-main-成员函数\">4、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 成员函数</a></li>\n <li><a href=\"#5cumulusserver-是-serverapplication-的子类\" id=\"markdown-toc-5cumulusserver-是-serverapplication-的子类\">5、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的子类</a></li>\n <li><a href=\"#6serverapplication-是-application-的子类\" id=\"markdown-toc-6serverapplication-是-application-的子类\">6、<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的子类</a></li>\n <li><a href=\"#7反初始化\" id=\"markdown-toc-7反初始化\">7、反初始化</a></li>\n </ul>\n </li>\n <li><a href=\"#二cumulusserver-各项交互功能的源码解读\" id=\"markdown-toc-二cumulusserver-各项交互功能的源码解读\">二、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 各项交互功能的源码解读</a> <ul>\n <li><a href=\"#1命令行选项设定\" id=\"markdown-toc-1命令行选项设定\">1、命令行选项设定</a></li>\n <li><a href=\"#2处理命令行选项\" id=\"markdown-toc-2处理命令行选项\">2、处理命令行选项</a></li>\n <li><a href=\"#6dump-logs\" id=\"markdown-toc-6dump-logs\">6、Dump logs</a></li>\n <li><a href=\"#3停止运行\" id=\"markdown-toc-3停止运行\">3、停止运行</a></li>\n <li><a href=\"#4载入配置\" id=\"markdown-toc-4载入配置\">4、载入配置</a></li>\n <li><a href=\"#5处理日志\" id=\"markdown-toc-5处理日志\">5、处理日志</a></li>\n </ul>\n </li>\n <li><a href=\"#三maincpp-的-main-函数源码分析\" id=\"markdown-toc-三maincpp-的-main-函数源码分析\">三、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数源码分析</a> <ul>\n <li><a href=\"#1maincpp-中的-main-函数中的-server\" id=\"markdown-toc-1maincpp-中的-main-函数中的-server\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中的 <code class=\"language-plaintext highlighter-rouge\">server</code></a></li>\n <li><a href=\"#2maincpp-中-main-函数的-serverstart\" id=\"markdown-toc-2maincpp-中-main-函数的-serverstart\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的 <code class=\"language-plaintext highlighter-rouge\">server.start()</code></a></li>\n <li><a href=\"#3回顾一下整个启动流程\" id=\"markdown-toc-3回顾一下整个启动流程\">3、回顾一下整个启动流程</a></li>\n <li><a href=\"#4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\" id=\"markdown-toc-4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>、<code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(...)</code> 函数源码</a></li>\n </ul>\n </li>\n</ul>\n\n<p>本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。首先要知道的是,OpenRTMFP/Cumulus 中使用到的库有 Poco、OpenSSL 和 Lua。</p>\n\n<h3 id=\"一cumulus-启动源码分析\">一、Cumulus 启动源码分析</h3>\n\n<h4 id=\"1maincpp-中的-main-函数\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数</h4>\n\n<p>入口在 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"kt\">int</span> <span class=\"n\">argc</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">argv</span><span class=\"p\">[])</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先检查内存泄露,不过目前这个开发中的项目还没有实现这个功能,只是个空函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">DetectMemoryLeak</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>然后会创建一个匿名的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 对象,并调用其 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,该函数由 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 从 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 中继承而来,而 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 由是从 <code class=\"language-plaintext highlighter-rouge\">Application</code> 继承而来的。<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 对象调用 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,实际是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数则是调用 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的函数,而该 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数则是先调用 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数,然后调用 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,然后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数。如果 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数被调用时抛出异常,则不会执行 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,但仍然会执行 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Runs the application by performing additional initializations</span>\n <span class=\"c1\">// and calling the main() method.</span>\n <span class=\"k\">return</span> <span class=\"nf\">CumulusServer</span><span class=\"p\">().</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"n\">argc</span><span class=\"p\">,</span> <span class=\"n\">argv</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2maincpp-中的-cumulusserver-构造函数\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer()</code> 构造函数</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的构造函数定义为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">CumulusServer</span><span class=\"p\">()</span><span class=\"o\">:</span> <span class=\"n\">_helpRequested</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span> <span class=\"c1\">// 显示帮助信息</span>\n <span class=\"n\">_pCirrus</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_middle</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">_isInteractive</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_pLogFile</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3maincpp-中的-cumulusserver-的-initialize-成员函数\">3、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 成员函数</h4>\n\n<p>在执行 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数之前,<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 会启动 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数,传入的参数就是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 自己,可以猜到 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> 的 <code class=\"language-plaintext highlighter-rouge\">run</code> 方法中,调用该函数时的参数是 <code class=\"language-plaintext highlighter-rouge\">this</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">initialize</span><span class=\"p\">(</span><span class=\"n\">Application</span><span class=\"o\">&</span> <span class=\"n\">self</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>调用父函数 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的初始化函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">initialize</span><span class=\"p\">(</span><span class=\"n\">self</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>再继续下面的源码分析之前,先要知道,根据 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> ,<code class=\"language-plaintext highlighter-rouge\">Application</code> 类有一些内置的配置属性,如下:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">application.path</code>: 可执行文件的绝对路径;</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.name</code>: 可执行文件的文件名(含扩展名);</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.baseName</code>: 可执行文件的文件名(不含扩展名)</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.dir</code>: 可执行文件的所在目录;</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.configDir</code>: 配置文件所在目录;</li>\n</ul>\n\n<p>所以下面就读取了可执行文件的所在目录,其中第二个参数表示默认值(即当前目录):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">string</span> <span class=\"n\">dir</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span> <span class=\"s\">\"./\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>然后读取配置文件,目录为上一句所得到的 <code class=\"language-plaintext highlighter-rouge\">dir</code>,文件名(不含扩展名)为 <code class=\"language-plaintext highlighter-rouge\">application.basename</code> 内置配置属性值,其默认值为 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>,然后加上点和扩展名 <code class=\"language-plaintext highlighter-rouge\">.ini</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">loadConfiguration</span><span class=\"p\">(</span><span class=\"n\">dir</span>\n <span class=\"o\">+</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.baseName\"</span><span class=\"p\">,</span> <span class=\"s\">\"CumulusServer\"</span><span class=\"p\">)</span>\n <span class=\"o\">+</span> <span class=\"s\">\".ini\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这样就加载完配置了。然后查看当前的进程是从命令行运行的(命令行是交互的,所以是 interactive),还是以 daemon 方式运行的,这个函数是ServerApplication的一个成员函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_isInteractive</span> <span class=\"o\">=</span> <span class=\"n\">isInteractive</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>然后获取表示日志文件所在目录的字符串,其中 <code class=\"language-plaintext highlighter-rouge\">logs.directory</code> 是外置配置属性(配置文件中),其默认值为上面获取到的可执行文件路径(一般为当前路径)与 <code class=\"language-plaintext highlighter-rouge\">logs</code> 的组合,即一般为当前目录下的 <code class=\"language-plaintext highlighter-rouge\">logs</code> 目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">string</span> <span class=\"nf\">logDir</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"logs.directory\"</span><span class=\"p\">,</span> <span class=\"n\">dir</span> <span class=\"o\">+</span> <span class=\"s\">\"logs\"</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>创建日志文件目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span><span class=\"p\">(</span><span class=\"n\">logDir</span><span class=\"p\">).</span><span class=\"n\">createDirectory</span><span class=\"p\">();</span>\n\n</code></pre></div></div>\n\n<p>日志文件绝对路径,<code class=\"language-plaintext highlighter-rouge\">logs</code> 为默认的日志文件名(不含扩展名的部分),扩展名用0:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logPath</span> <span class=\"o\">=</span> <span class=\"n\">logDir</span> <span class=\"o\">+</span> <span class=\"s\">\"/\"</span> <span class=\"o\">+</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"logs.name\"</span><span class=\"p\">,</span> <span class=\"s\">\"log\"</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"s\">\".\"</span><span class=\"p\">;</span>\n <span class=\"n\">_pLogFile</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nf\">File</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"s\">\"0\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>用日志流打开日志文件(方式为追加写入):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">open</span><span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">path</span><span class=\"p\">(),</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">in</span> <span class=\"o\">|</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">ate</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Logs</code> 是一个方法类(其中的 <code class=\"language-plaintext highlighter-rouge\">public</code> 函数都是静态的),<code class=\"language-plaintext highlighter-rouge\">SetLogger</code> 的作用就是将Logs中的似有静态成员设置为某个 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Logger</code> 对象(由于 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 继承了 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Logger</code>)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetLogger</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4maincpp-中的-cumulusserver-的-main-成员函数\">4、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 成员函数</h4>\n\n<p>OpenRTMFP/Cumulus 是一个基于 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> 的服务端应用(准确的说是基于 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::ServerApplication</code> 的服务端应用)。如果没有特殊的启动要求,可以调用 <code class=\"language-plaintext highlighter-rouge\">Poco/Application.h</code> 中定义的宏 <code class=\"language-plaintext highlighter-rouge\">POCO_APP_MAIN</code> 来完成初始化、日志和启动(该宏会根据不同的平台启用不同的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数)。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">run()</code> 函数在调用完 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数后,会调用 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,该 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的定义在 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">>&</span> <span class=\"n\">args</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>首先看是否是要求帮助信息,<code class=\"language-plaintext highlighter-rouge\">displayHelp</code> 是借助 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::HelpFormatter</code> 实现的,<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的构造函数会在调用时将 <code class=\"language-plaintext highlighter-rouge\">_helpRequested</code> 设置为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_helpRequested</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">displayHelp</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是,则进入启动状态,首先创建一个 <code class=\"language-plaintext highlighter-rouge\">RTMFPServerParams</code> 对象 <code class=\"language-plaintext highlighter-rouge\">params</code>,用来存储 OpenRTMFP/CumulusServer 的基本配置信息。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">RTMFPServerParams</span> <span class=\"n\">params</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">params</code> 存储 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的端口号和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的端口号:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"port\"</span><span class=\"p\">,</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span><span class=\"p\">);</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">edgesPort</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"edges.port\"</span><span class=\"p\">,</span>\n <span class=\"n\">RTMFP_DEFAULT_PORT</span><span class=\"o\">+</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getBool</span><span class=\"p\">(</span><span class=\"s\">\"edges.activated\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">edgesPort</span><span class=\"o\">==</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"edges.port must have a positive value if \\\n edges.activated is true. Server edges is \\\n disactivated.\"</span><span class=\"p\">);</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesPort</span><span class=\"o\">=</span><span class=\"n\">edgesPort</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">_pCirrus</code> 为 <code class=\"language-plaintext highlighter-rouge\">SocketAddress</code> 的成员,是封装IP地址和端口号的对象。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span> <span class=\"o\">=</span> <span class=\"n\">_pCirrus</span><span class=\"p\">;</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">middle</span> <span class=\"o\">=</span> <span class=\"n\">_middle</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>UDB 所使用的缓冲区大小:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"udpBufferSize\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span>\n <span class=\"s\">\"keepAliveServer\"</span><span class=\"p\">,</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"keepAlivePeer\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>失败前 CumulusEdge 的尝试次数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span>\n <span class=\"s\">\"edges.attemptsBeforeFallback\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Server</span> <span class=\"nf\">server</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span><span class=\"s\">\"./\"</span><span class=\"p\">),</span>\n <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span><span class=\"n\">config</span><span class=\"p\">());</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">waitForTerminationRequest()</code> 函数是 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中必须调用的,意为等待终止运行的操作请求。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// wait for CTRL-C or kill</span>\n <span class=\"n\">waitForTerminationRequest</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>一旦接收到终止操作的请求,就会执行下面这句,用以退出 OpenRTMFP/Cumulus 的运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Stop the server</span>\n <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">stop</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">catch</code> 一些可能产生的异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"Configuration problem : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">what</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer unknown error\"</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>OpenRTMFP/CumulusServer 停止运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"n\">Application</span><span class=\"o\">::</span><span class=\"n\">EXIT_OK</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5cumulusserver-是-serverapplication-的子类\">5、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的子类</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 对其子类有如下要求:</p>\n\n<ul>\n <li>Subsystems must be registered in the constructor.</li>\n <li>All non-trivial initializations must be made in the <code class=\"language-plaintext highlighter-rouge\">initialize()</code>` method.</li>\n <li>At the end of the <code class=\"language-plaintext highlighter-rouge\">main()</code> method, <code class=\"language-plaintext highlighter-rouge\">waitForTerminationRequest()</code> should be called.</li>\n</ul>\n\n<h4 id=\"6serverapplication-是-application-的子类\">6、<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的子类</h4>\n\n<p>Application 对其子类的要求是,如下这些成员函数必须被覆盖:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">initialize()</code> (the one-argument, protected variant):上一篇已介绍过。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">uninitialize()</code>:下面会介绍,Application 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数会在调用 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">reinitialize()</code></li>\n <li><code class=\"language-plaintext highlighter-rouge\">defineOptions()</code>:定义命令行启动选项。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">handleOption()</code>:响应相应的命令行选项。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">main()</code></li>\n</ul>\n\n<h4 id=\"7反初始化\">7、反初始化</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是继承 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的,<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是继承 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的。<code class=\"language-plaintext highlighter-rouge\">Application</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数会先调用 <code class=\"language-plaintext highlighter-rouge\">initialize()</code>,然后调用 <code class=\"language-plaintext highlighter-rouge\">main()</code>,最后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize</code>。最后这个反初始化过程,在 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 就是直接调用父类的 <code class=\"language-plaintext highlighter-rouge\">uninitialize</code> 函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">uninitialize</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">uninitialize</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"二cumulusserver-各项交互功能的源码解读\">二、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 各项交互功能的源码解读</h3>\n\n<h4 id=\"1命令行选项设定\">1、命令行选项设定</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的命令行选项有:<code class=\"language-plaintext highlighter-rouge\">log(l)</code>、<code class=\"language-plaintext highlighter-rouge\">dump(d)</code>、<code class=\"language-plaintext highlighter-rouge\">cirrus(c)</code>、<code class=\"language-plaintext highlighter-rouge\">middle(m)</code>、<code class=\"language-plaintext highlighter-rouge\">help(h)</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">defineOptions</span><span class=\"p\">(</span><span class=\"n\">OptionSet</span><span class=\"o\">&</span> <span class=\"n\">options</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">defineOptions</span><span class=\"p\">(</span><span class=\"n\">options</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>设定日志级别(0 - 8,默认是 6,表示 info 级别)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"log\"</span><span class=\"p\">,</span> <span class=\"s\">\"l\"</span><span class=\"p\">,</span> <span class=\"s\">\"Log level argument, must be beetween 0 and 8 : \\\n nothing, fatal, critic, error, warn, note, info, debug, trace. \\\n Default value is 6 (info), all logs until info level are displayed.\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">required</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">argument</span><span class=\"p\">(</span><span class=\"s\">\"level\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>其他一些选项:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"dump\"</span><span class=\"p\">,</span> <span class=\"s\">\"d\"</span><span class=\"p\">,</span> <span class=\"s\">\"Enables packet traces in logs. Optional arguments \\\n are 'middle' or 'all' respectively to displays just middle packet \\\n process or all packet process. If no argument is given, just outside \\\n packet process will be dumped.\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">,</span><span class=\"s\">\"middle|all\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"cirrus\"</span><span class=\"p\">,</span> <span class=\"s\">\"c\"</span><span class=\"p\">,</span> <span class=\"s\">\"Cirrus address to activate a 'man-in-the-middle' \\\n developer mode in bypassing flash packets to the official cirrus \\\n server of your choice, it's a instable mode to help Cumulus developers, \\\n </span><span class=\"se\">\\\"</span><span class=\"s\">p2p.rtmfp.net:10000</span><span class=\"se\">\\\"</span><span class=\"s\"> for example. By adding the 'dump' argument, \\\n you will able to display Cirrus/Flash packet exchange in your logs \\\n (see 'dump' argument).\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">,</span><span class=\"s\">\"address\"</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"middle\"</span><span class=\"p\">,</span> <span class=\"s\">\"m\"</span><span class=\"p\">,</span><span class=\"s\">\"Enables a 'man-in-the-middle' developer mode \\\n between two peers. It's a instable mode to help Cumulus developers. \\\n By adding the 'dump' argument, you will able to display Flash/Flash \\\n packet exchange in your logs (see 'dump' argument).\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>显示帮助信息的选项:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"help\"</span><span class=\"p\">,</span> <span class=\"s\">\"h\"</span><span class=\"p\">,</span> <span class=\"s\">\"Displays help information about command-line usage.\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">required</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">OptionSet</code> 是 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::OptionSet</code>,调用addOption可以向其中增加选项Option。其中required和repeatable表示:</p>\n\n<p>Sets whether the option is required (flag == true) or optional (flag == false).\nReturns true if the option can be specified more than once, or false if at most once.\n当需要显示帮助信息时,调用如下函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">displayHelp</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">HelpFormatter</span> <span class=\"n\">helpFormatter</span><span class=\"p\">(</span><span class=\"n\">options</span><span class=\"p\">());</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setCommand</span><span class=\"p\">(</span><span class=\"n\">commandName</span><span class=\"p\">());</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setUsage</span><span class=\"p\">(</span><span class=\"s\">\"OPTIONS\"</span><span class=\"p\">);</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setHeader</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer, open source RTMFP server\"</span><span class=\"p\">);</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">cout</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">setCommand()</code>: Sets the command name.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">setUsage()</code>: Sets the usage string.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">setHeader()</code>: Sets the header string.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">format()</code>: Writes the formatted help text to the given stream.</li>\n</ul>\n\n<h4 id=\"2处理命令行选项\">2、处理命令行选项</h4>\n\n<p>参数是选项名和选项值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">handleOption</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">handleOption</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果选项是帮助:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"help\"</span><span class=\"p\">)</span>\n <span class=\"n\">_helpRequested</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"cirrus\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">URI</span> <span class=\"n\">uri</span><span class=\"p\">(</span><span class=\"s\">\"rtmfp://\"</span><span class=\"o\">+</span><span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">SocketAddress</span><span class=\"p\">(</span><span class=\"n\">uri</span><span class=\"p\">.</span><span class=\"n\">getHost</span><span class=\"p\">(),</span><span class=\"n\">uri</span><span class=\"p\">.</span><span class=\"n\">getPort</span><span class=\"p\">());</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"Mode 'man in the middle' : the exchange will bypass to '%s'\"</span><span class=\"p\">,</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Mode 'man in the middle' error : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">message</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果选项是dump日志:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"dump\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">==</span> <span class=\"s\">\"all\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">ALL</span><span class=\"p\">);</span>\n <span class=\"k\">else</span> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">==</span> <span class=\"s\">\"middle\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">MIDDLE</span><span class=\"p\">);</span>\n <span class=\"k\">else</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">EXTERNAL</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果选项是middle:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"middle\"</span><span class=\"p\">)</span>\n <span class=\"n\">_middle</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果选项是log,表示设定日志级别:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"log\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetLevel</span><span class=\"p\">(</span><span class=\"n\">atoi</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">c_str</span><span class=\"p\">()));</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"6dump-logs\">6、Dump logs</h4>\n\n<p>先加一个作用域锁,然后再向日志流写数据。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">dumpHandler</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">data</span><span class=\"p\">,</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_logMutex</span><span class=\"p\">);</span>\n <span class=\"n\">cout</span><span class=\"p\">.</span><span class=\"n\">write</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">data</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">write</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">data</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">manageLogFile</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>调用 manageLogFile,主要做一些日志大小超出限制的处理。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">manageLogFile</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先判断是否超过日志文件的大小上线,LOG_SIZE是1000000字节(即约 1 MB)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">getSize</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"n\">LOG_SIZE</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"kt\">int</span> <span class=\"n\">num</span> <span class=\"o\">=</span> <span class=\"mi\">10</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>打开新日志文件:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span> <span class=\"nf\">file</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"s\">\"10\"</span><span class=\"p\">);</span>\n\n</code></pre></div></div>\n\n<p>如果该文件已经存在,则先删除:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">exists</span><span class=\"p\">())</span>\n <span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">remove</span><span class=\"p\">();</span>\n\n <span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">--</span><span class=\"n\">num</span> <span class=\"o\">>=</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">file</span> <span class=\"o\">=</span> <span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"n\">NumberFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">num</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">exists</span><span class=\"p\">())</span>\n <span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">renameTo</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"n\">NumberFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">num</span> <span class=\"o\">+</span> <span class=\"mi\">1</span><span class=\"p\">));</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">open</span><span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">path</span><span class=\"p\">(),</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">in</span> <span class=\"o\">|</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">ate</span><span class=\"p\">);</span>\n <span class=\"err\">}</span> \n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3停止运行\">3、停止运行</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 继承了 <code class=\"language-plaintext highlighter-rouge\">ApplicationKiller</code>,该类中有纯虚函数 <code class=\"language-plaintext highlighter-rouge\">kill()</code> 需要被实现,于是有:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">kill</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">terminate</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ApplicationKiller</code> 的定义在 <code class=\"language-plaintext highlighter-rouge\">ApplicationKiller.h</code> 中,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ApplicationKiller</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"p\">(){}</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">ApplicationKiller</span><span class=\"p\">(){}</span>\n \n <span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">kill</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"4载入配置\">4、载入配置</h4>\n\n<p>在initialize()函数中调用,上一篇已提到过。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">loadConfiguration</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">path</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">loadConfiguration</span><span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">);</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5处理日志\">5、处理日志</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">logHandler</span><span class=\"p\">(</span><span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">TID</span> <span class=\"n\">threadId</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">threadName</span><span class=\"p\">,</span>\n <span class=\"n\">Priority</span> <span class=\"n\">priority</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"kt\">char</span> <span class=\"o\">*</span><span class=\"n\">filePath</span><span class=\"p\">,</span>\n <span class=\"kt\">long</span> <span class=\"n\">line</span><span class=\"p\">,</span> \n <span class=\"k\">const</span> <span class=\"kt\">char</span> <span class=\"o\">*</span><span class=\"n\">text</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>作用域锁:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_logMutex</span><span class=\"p\">);</span>\n \n <span class=\"n\">Path</span> <span class=\"nf\">path</span><span class=\"p\">(</span><span class=\"n\">filePath</span><span class=\"p\">);</span>\n <span class=\"n\">string</span> <span class=\"n\">file</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getExtension</span><span class=\"p\">()</span> <span class=\"o\">==</span> <span class=\"s\">\"lua\"</span><span class=\"p\">)</span>\n <span class=\"n\">file</span> <span class=\"o\">+=</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">directory</span><span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">depth</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"s\">\"/\"</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是命令行交互模式(即不是 daemon 模式):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_isInteractive</span><span class=\"p\">)</span>\n <span class=\"n\">printf</span><span class=\"p\">(</span><span class=\"s\">\"%s %s[%ld] %s</span><span class=\"se\">\\n</span><span class=\"s\">\"</span><span class=\"p\">,</span>\n <span class=\"n\">g_logPriorities</span><span class=\"p\">[</span><span class=\"n\">priority</span> <span class=\"o\">-</span> <span class=\"mi\">1</span><span class=\"p\">],</span>\n <span class=\"p\">(</span><span class=\"n\">file</span> <span class=\"o\">+</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getBaseName</span><span class=\"p\">()).</span><span class=\"n\">c_str</span><span class=\"p\">(),</span>\n <span class=\"n\">line</span><span class=\"p\">,</span>\n <span class=\"n\">text</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>向日志流输出一句日志:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logStream</span> <span class=\"o\"><<</span> <span class=\"n\">DateTimeFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">LocalDateTime</span><span class=\"p\">(),</span><span class=\"s\">\"%d/%m %H:%M:%S.%c \"</span><span class=\"p\">)</span>\n <span class=\"o\"><<</span> <span class=\"n\">g_logPriorities</span><span class=\"p\">[</span><span class=\"n\">priority</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'\\t'</span> <span class=\"o\"><<</span> <span class=\"n\">threadName</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'('</span> <span class=\"o\"><<</span> <span class=\"n\">threadId</span> <span class=\"o\"><<</span> <span class=\"s\">\")</span><span class=\"se\">\\t</span><span class=\"s\">\"</span>\n <span class=\"o\"><<</span> <span class=\"p\">(</span><span class=\"n\">file</span> <span class=\"o\">+</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getFileName</span><span class=\"p\">())</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'['</span> <span class=\"o\"><<</span> <span class=\"n\">line</span> <span class=\"o\"><<</span> <span class=\"s\">\"] \"</span> \n <span class=\"o\"><<</span> <span class=\"n\">text</span> <span class=\"o\"><<</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">endl</span><span class=\"p\">;</span>\n \n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">flush</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>日志文件的善后处理(主要处理文件大小限制可能产生的问题):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">manageLogFile</span><span class=\"p\">();</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三maincpp-的-main-函数源码分析\">三、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数源码分析</h3>\n\n<h4 id=\"1maincpp-中的-main-函数中的-server\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中的 <code class=\"language-plaintext highlighter-rouge\">server</code></h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中真正启动的是 <code class=\"language-plaintext highlighter-rouge\">server</code>,它继承自 <code class=\"language-plaintext highlighter-rouge\">Cumulus::RTMFPServer</code>,而 <code class=\"language-plaintext highlighter-rouge\">Cumulus::RTMFPServer</code> 又继承自 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Startable</code>、<code class=\"language-plaintext highlighter-rouge\">Cumulus::Gateway</code>、<code class=\"language-plaintext highlighter-rouge\">Cumulus::Handler</code>。而 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Startable</code> 继承自 <code class=\"language-plaintext highlighter-rouge\">Poco::Runnable</code>,所以其是一个可以运行的线程。在 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusServer</code> 中,这是主线程。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span> <span class=\"nf\">server</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span> <span class=\"s\">\"./\"</span><span class=\"p\">),</span> <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span> <span class=\"n\">config</span><span class=\"p\">());</span>\n<span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer/Server.h</code> 中定义的,其构造函数的原型为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">root</span><span class=\"p\">,</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"o\">&</span> <span class=\"n\">applicationKiller</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">AbstractConfiguration</span><span class=\"o\">&</span> <span class=\"n\">configurations</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>个参数含义如下:</p>\n\n<blockquote>\n <p>The Path Root for the Server Application Killer for Termanting the Server Application Server Configuration</p>\n</blockquote>\n\n<p>距离来说,在我的 Worksapce 中:</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">root</code> 是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 构造函数的初始化列表极长:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span><span class=\"o\">::</span><span class=\"n\">Server</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">root</span><span class=\"p\">,</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"o\">&</span> <span class=\"n\">applicationKiller</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">AbstractConfiguration</span><span class=\"o\">&</span> <span class=\"n\">configurations</span><span class=\"p\">)</span> \n <span class=\"o\">:</span> <span class=\"n\">_blacklist</span><span class=\"p\">(</span><span class=\"n\">root</span> <span class=\"o\">+</span> <span class=\"s\">\"blacklist\"</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">),</span>\n <span class=\"n\">_applicationKiller</span><span class=\"p\">(</span><span class=\"n\">applicationKiller</span><span class=\"p\">),</span>\n <span class=\"n\">_hasOnRealTime</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_pService</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">luaMail</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"o\">=</span><span class=\"n\">Script</span><span class=\"o\">::</span><span class=\"n\">CreateState</span><span class=\"p\">(),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"smtp.host\"</span><span class=\"p\">,</span><span class=\"s\">\"localhost\"</span><span class=\"p\">),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"smtp.port\"</span><span class=\"p\">,</span><span class=\"n\">SMTPSession</span><span class=\"o\">::</span><span class=\"n\">SMTP_PORT</span><span class=\"p\">),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"smtp.timeout\"</span><span class=\"p\">,</span><span class=\"mi\">60</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>下面调用 <code class=\"language-plaintext highlighter-rouge\">Poco::File</code> 创建目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span><span class=\"p\">((</span><span class=\"n\">string</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">WWWPath</span> <span class=\"o\">=</span> <span class=\"n\">root</span> <span class=\"o\">+</span> <span class=\"s\">\"www\"</span><span class=\"p\">).</span><span class=\"n\">createDirectory</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>因为 <code class=\"language-plaintext highlighter-rouge\">roor</code> 是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 目录,所以 <code class=\"language-plaintext highlighter-rouge\">WWWPath</code> 就是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/www</code> 目录。然后初始化 <code class=\"language-plaintext highlighter-rouge\">GlobalTable</code>,这个 <code class=\"language-plaintext highlighter-rouge\">GlobalTable</code> 是和 Lua 有关的东东,这里暂不细说,先知道与 Lua 相关就好。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Service</span><span class=\"o\">::</span><span class=\"n\">InitGlobalTable</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>下面就涉及到了 Lua script 了:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SCRIPT_BEGIN</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">)</span>\n <span class=\"n\">SCRIPT_CREATE_PERSISTENT_OBJECT</span><span class=\"p\">(</span><span class=\"n\">Invoker</span><span class=\"p\">,</span><span class=\"n\">LUAInvoker</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span>\n <span class=\"n\">readNextConfig</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">,</span><span class=\"n\">configurations</span><span class=\"p\">,</span><span class=\"s\">\"\"</span><span class=\"p\">);</span>\n <span class=\"n\">lua_setglobal</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">,</span><span class=\"s\">\"cumulus.configs\"</span><span class=\"p\">);</span>\n <span class=\"n\">SCRIPT_END</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">SCRIPT_BEGIN</code>、<code class=\"language-plaintext highlighter-rouge\">SCRIPT_CREATE_PERSISTENT_OBJECT和SCRIPT_END</code> 都是宏,其定义在 <code class=\"language-plaintext highlighter-rouge\">Script.h</code> 文件中,如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#define SCRIPT_BEGIN(STATE) \\\n if (lua_State* __pState = STATE) { \\\n const char* __error=NULL;\n \n#define SCRIPT_CREATE_PERSISTENT_OBJECT(TYPE,LUATYPE,OBJ) \\\n Script::WritePersistentObject<TYPE,LUATYPE>(__pState,OBJ); \\\n lua_pop(__pState,1);\n \n#define SCRIPT_END }\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">SCRIPT_BEGIN和SCRIPT_END</code> 经常用到,当与 Lua 相关的操作出现时,都会以这两个宏作为开头和结尾。</p>\n\n<h4 id=\"2maincpp-中-main-函数的-serverstart\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的 <code class=\"language-plaintext highlighter-rouge\">server.start()</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">RTMFPServerParams</span><span class=\"o\">&</span> <span class=\"n\">params</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusServer</code> 正在运行,则返回并终止启动。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer server is yet running, call stop method before\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定端口号,如果端口号为 0,则返回并终止启动。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_port</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_port</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer port must have a positive value\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusEdge</code> 的端口号,如果其端口号与 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusSever</code> 端口号相同,则返回并终止启动:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_edgesPort</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesPort</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_port</span> <span class=\"o\">==</span> <span class=\"n\">_edgesPort</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer port must different than RTMFPServer edges.port\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Cirrus:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_freqManage</span> <span class=\"o\">=</span> <span class=\"mi\">2000000</span><span class=\"p\">;</span> <span class=\"c1\">// 2 sec by default</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">Target</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span><span class=\"p\">);</span>\n <span class=\"n\">_freqManage</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span> <span class=\"c1\">// no waiting, direct process in the middle case!</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer started in man-in-the-middle mode with server %s \\\n (unstable debug mode)\"</span><span class=\"p\">,</span> <span class=\"n\">_pCirrus</span><span class=\"o\">-></span><span class=\"n\">address</span><span class=\"p\">.</span><span class=\"n\">toString</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>middle:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_middle</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">middle</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_middle</span><span class=\"p\">)</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer started in man-in-the-middle mode between peers \\\n (unstable debug mode)\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>UDP Buffer:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"o\">==</span><span class=\"mi\">0</span> <span class=\"o\">?</span> \n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">getReceiveBufferSize</span><span class=\"p\">()</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"p\">;</span>\n \n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">setReceiveBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">setSendBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">setReceiveBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">setSendBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n \n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Socket buffer receving/sending size = %u/%u\"</span><span class=\"p\">,</span>\n <span class=\"n\">udpBufferSize</span><span class=\"p\">,</span>\n <span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n \n <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\"><</span> <span class=\"mi\">5</span> <span class=\"o\">?</span> <span class=\"mi\">5000</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\"><</span> <span class=\"mi\">5</span> <span class=\"o\">?</span> <span class=\"mi\">5000</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"n\">UInt8</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">edgesAttemptsBeforeFallback</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span><span class=\"p\">;</span>\n \n <span class=\"n\">setPriority</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">threadPriority</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>启动线程,进入循环运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">();</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>上句具体的源码实现为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果在运行则返回并终止启动。然后加一个局部锁。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果不得不join()到主线程中,那就join()吧</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>然后就运行这个线程吧:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_terminate</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3回顾一下整个启动流程\">3、回顾一下整个启动流程</h4>\n\n<p>到此我们先回顾一下启动过程:</p>\n\n<p>从 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的启动入口 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数开始,创建 <code class=\"language-plaintext highlighter-rouge\">Server</code> 对象并启动(调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 函数)。<code class=\"language-plaintext highlighter-rouge\">Server::start()</code> 中调用其父类(<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code>)的父类(<code class=\"language-plaintext highlighter-rouge\">Startable</code>)的方法 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code> 开启线程。\n调用 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code> 函数后,开启线城时传入的参数为 <code class=\"language-plaintext highlighter-rouge\">*this</code>,所以就会运行 <code class=\"language-plaintext highlighter-rouge\">Startable::run()</code>;</p>\n\n<h4 id=\"4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>、<code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(...)</code> 函数源码</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Startable::run()</code> 调用 <code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 函数,但这个函数被 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖,所以会运行 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>,其源码如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP server starts on %u port\"</span><span class=\"p\">,</span><span class=\"n\">_port</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果CumulusEdge:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP edges server starts on %u port\"</span><span class=\"p\">,</span><span class=\"n\">_edgesPort</span><span class=\"p\">);</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">onStart</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>运行线程:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>处理异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">what</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer unknown error\"</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果跳出了,则终止运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">onStop</span><span class=\"p\">();</span>\n \n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP server stops\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>该函数内部又会调用父类的 <code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 函数,该函数调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>它是一个纯虚函数,由 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 实现。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 会调用 <code class=\"language-plaintext highlighter-rouge\">void run(const volatile bool& terminate)</code> 方法,该方法被 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">run</span><span class=\"p\">(</span><span class=\"n\">_terminate</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"o\">!</span><span class=\"n\">_terminate</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的 <code class=\"language-plaintext highlighter-rouge\">run(const volatile bool &terminate)</code> 方法。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">...</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</title>\n \t<meta name=\"description\" content=\"RTMFP 是 Adobe 开发的基于 UDP 协议的实时传输媒体流协议,支持 P2P 传输,具有较高的实时性和安全性。它的主要应用场景是视频通信、语音通信和网络游戏。OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。Cumulus 是一个基于 OpenRTMFP 的服务器,提供 RTMFP 服务。它具有轻量级、跨平台和可扩展的特点,并且还提供了负载均衡和可扩展性解决方案。YY 语音的 Web 端音视频流媒体能力,正是基于 RTMFP 协议做的迭代优化实现的。本文是船长关于这个系列文章的第一篇。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</h2>\t\t\n\t<time datetime=\"2012-04-09T18:57:19+00:00\" class=\"by-line\">09 Apr 2012, 广州 | 作者 麦克船长 | 总计 8401 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一rtmfp是什么\" id=\"markdown-toc-一rtmfp是什么\">一、RTMFP 是什么?</a> <ul>\n <li><a href=\"#文件分享-p2p-和实时流媒体-p2p-的区别是什么\" id=\"markdown-toc-文件分享-p2p-和实时流媒体-p2p-的区别是什么\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</a></li>\n <li><a href=\"#rtmfp-和-rtmp-之间的区别是什么\" id=\"markdown-toc-rtmfp-和-rtmp-之间的区别是什么\">RTMFP 和 RTMP 之间的区别是什么?</a></li>\n <li><a href=\"#flash-player-支持-rtmfp-吗\" id=\"markdown-toc-flash-player-支持-rtmfp-吗\">Flash Player 支持 RTMFP 吗?</a></li>\n <li><a href=\"#cumulus-使用-adobe-的-cirrus-key-吗\" id=\"markdown-toc-cumulus-使用-adobe-的-cirrus-key-吗\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</a></li>\n <li><a href=\"#这个开源项目合法吗\" id=\"markdown-toc-这个开源项目合法吗\">这个开源项目合法吗?</a></li>\n </ul>\n </li>\n <li><a href=\"#二openrtmfp和cumulus\" id=\"markdown-toc-二openrtmfp和cumulus\">二、OpenRTMFP 和 Cumulus</a></li>\n <li><a href=\"#三入门介绍与部署cumulusserver\" id=\"markdown-toc-三入门介绍与部署cumulusserver\">三、入门介绍与部署 CumulusServer</a> <ul>\n <li><a href=\"#1背景介绍\" id=\"markdown-toc-1背景介绍\">1、背景介绍</a></li>\n <li><a href=\"#2准备工作\" id=\"markdown-toc-2准备工作\">2、准备工作</a></li>\n <li><a href=\"#3安装\" id=\"markdown-toc-3安装\">3、安装</a> <ul>\n <li><a href=\"#31外部依赖的安装\" id=\"markdown-toc-31外部依赖的安装\">3.1、外部依赖的安装</a></li>\n <li><a href=\"#32安装openrtmfpcumulus\" id=\"markdown-toc-32安装openrtmfpcumulus\">3.2、安装 OpenRTMFP/Cumulus</a></li>\n </ul>\n </li>\n <li><a href=\"#4配置\" id=\"markdown-toc-4配置\">4、配置</a></li>\n <li><a href=\"#5启动\" id=\"markdown-toc-5启动\">5、启动</a></li>\n <li><a href=\"#6基本使用\" id=\"markdown-toc-6基本使用\">6、基本使用</a></li>\n <li><a href=\"#7扩展cumulusserverserverapplication\" id=\"markdown-toc-7扩展cumulusserverserverapplication\">7、扩展 CumulusServer(Server Application)</a></li>\n </ul>\n </li>\n <li><a href=\"#三用lua编写helloworld应用扩展cumulusserver\" id=\"markdown-toc-三用lua编写helloworld应用扩展cumulusserver\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</a> <ul>\n <li><a href=\"#1server-side\" id=\"markdown-toc-1server-side\">1、Server-side</a> <ul>\n <li><a href=\"#11serverconfiguration\" id=\"markdown-toc-11serverconfiguration\">1.1、Server configuration</a></li>\n <li><a href=\"#12applicationfile\" id=\"markdown-toc-12applicationfile\">1.2、Application file</a></li>\n </ul>\n </li>\n <li><a href=\"#2client-side\" id=\"markdown-toc-2client-side\">2、Client-side</a></li>\n <li><a href=\"#3运行结果\" id=\"markdown-toc-3运行结果\">3、运行结果</a></li>\n <li><a href=\"#4远程测试一个免费的测试服务器\" id=\"markdown-toc-4远程测试一个免费的测试服务器\">4、远程测试:一个免费的测试服务器</a></li>\n </ul>\n </li>\n</ul>\n\n<h3 id=\"一rtmfp是什么\">一、RTMFP 是什么?</h3>\n\n<p>Real-Time Media Flow Protocol(RTMFP)是 Adobe 开发的一种基于 UDP 并支持 P2P 的实时传输媒体流。主要特点是:高传输效率(可以使用压缩和算法来优化流量从而提高传输效率)、高实时性(可以保证媒体流的实时性使得视频通信和其他实时通信更加流畅)、支持 P2P 传输(减少对服务器的依赖从而减少带宽和服务器资源消耗)、高安全性(加密媒体流从而保证其安全性)。</p>\n\n<p>RTMFP 的主要应用场景包括:视频通信(视频聊天和视频会议)、语音通信(语音聊天、电话)、网络游戏。不过 RTMFP 目前仅有 Adobe 开发的版本,所以它并不是个开源项目,而是个商业化服务。那么有没有开源版本呢?</p>\n\n<h4 id=\"文件分享-p2p-和实时流媒体-p2p-的区别是什么\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</h4>\n\n<p>RTMFP 是一个 P2P 系统,但它仅针对实时通信时直接用户到用户之间的通信而设计,不能用于多个对等方之间进行文件共享(使用分段下载)。Facebook 在其 Pipe 应用中使用此协议将大文件直接在两个用户之间传输。</p>\n\n<h4 id=\"rtmfp-和-rtmp-之间的区别是什么\">RTMFP 和 RTMP 之间的区别是什么?</h4>\n\n<p>RTMP 是实时消息协议,RTMFP 代表实时媒体流协议。RTMFP 基于用户数据报协议(UDP),而 RTMP 基于传输控制协议(TCP)。\n与 RTMP 不同,RTMFP 还支持直接从一个 Adobe Flash Player 传输数据到另一个,而无需经过服务器。</p>\n\n<h4 id=\"flash-player-支持-rtmfp-吗\">Flash Player 支持 RTMFP 吗?</h4>\n\n<p>RTMFP 是基于用户数据报协议(UDP)的,而 RTMP 是基于传输控制协议(TCP)的。与 RTMP 不同,RTMFP 还支持直接在两个 Adobe Flash Player 之间发送数据,而不经过服务器。Flash Player 10.0 仅允许一对一通信进行 P2P,但从 10.1 开始允许应用程序级别的多播。Flash Player 查找适当的分发路由(覆盖网络),并可以将其分发到通过 P2P 连接的组。</p>\n\n<h4 id=\"cumulus-使用-adobe-的-cirrus-key-吗\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</h4>\n\n<p>不!当然,这是Cumulus的主要目标:成为Cirrus GPL的替代品。唯一的限制是:你的CPU,内存和单台机器的端口数。</p>\n\n<h4 id=\"这个开源项目合法吗\">这个开源项目合法吗?</h4>\n\n<p>在美国,数字千年版权法(Digital Millennium Copyright Act)规定,逆向工程用于协议互操作性是合法的。你可以在 WikiPedia 上查看相关讨论:</p>\n\n<ul>\n <li>http://en.wikipedia.org/wiki/Real_Time_Media_Flow_Protocol</li>\n <li>http://en.wikipedia.org/wiki/Proprietary_protocol</li>\n <li>http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act</li>\n</ul>\n\n<p>当逆向工程的目的是协议互操作性时,有法律先例。在美国,数字千年版权法(Digital Millennium Copyright Act)为逆向工程软件以使其与其他软件互操作提供了安全保障。</p>\n\n<h3 id=\"二openrtmfp和cumulus\">二、OpenRTMFP 和 Cumulus</h3>\n\n<p>OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。它包含了 RTMFP 协议的实现,以及一些额外的功能,如媒体流传输、P2P 通信、脚本引擎和数据存储。</p>\n\n<p>Cumulus 是一个基于 OpenRTMFP 的服务器,是一个完整的开源且跨平台的 RTMFP 服务器,可通过脚本进行扩展。CumulusServer 根据 GPL 许可在考虑以下 4 个概念的情况下开发:速度、轻量、跨平台和可扩展。尽管尚未发布版本,但只有在 CumulusServer 经过测试和批准后才会将代码推送到 github。实际上,主要稳定的功能有:</p>\n\n<ul>\n <li>P2P rendez-vous service</li>\n <li>live streaming</li>\n <li>RPC、pull、push exchange,实际上客户端和服务器之间的所有 AMF 可能交换</li>\n <li>脚本引擎,用于创建自己的应用服务器或扩展 Cumulus 的功能</li>\n <li>可扩展性和负载均衡解决方案</li>\n</ul>\n\n<p>下面的内容是本篇 blog 的重点,包括两部分:先是 OpenRTMFP 应用的核心 CumulusServer 的入门介绍与部署,然后用 Lua 编写 HelloWorld 应用扩展 CumulusServer,我们开始吧!</p>\n\n<h3 id=\"三入门介绍与部署cumulusserver\">三、入门介绍与部署 CumulusServer</h3>\n\n<h4 id=\"1背景介绍\">1、背景介绍</h4>\n\n<p>OpenRTMFP 可以帮助你实现 Flash 的实时应用的高并发扩展,OpenRTMFP/Cumulus 是基于 GNU General Public License 的。</p>\n\n<p>POCO:POrtable COmponents,是一个强大的开源 C++ 库。其在 C++ 开发中的角色,相当于 Java Class Library、苹果的 Cocoa、.NET framework。</p>\n\n<h4 id=\"2准备工作\">2、准备工作</h4>\n\n<p>下载:</p>\n\n<table>\n <thead>\n <tr>\n <th><strong>External Dependencies</strong></th>\n <th><strong>Official Site</strong></th>\n <th><strong>Windows</strong></th>\n <th><strong>Linux/OSX</strong></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>OpenSSL</td>\n <td><a href=\"http://www.slproweb.com/products/Win32OpenSSL.html\">Official Site</a></td>\n <td><a href=\"http://www.slproweb.com/download/Win32OpenSSL_Light-1_0_1.exe\">Download</a></td>\n <td><a href=\"http://www.openssl.org/source/openssl-1.0.1.tar.gz\">Download</a></td>\n </tr>\n <tr>\n <td>Lua</td>\n <td><a href=\"http://www.lua.org/\">Official Site</a></td>\n <td><a href=\"http://luaforwindows.googlecode.com/files/LuaForWindows_v5.1.4-45.exe\">Download</a></td>\n <td><a href=\"http://www.lua.org/ftp/lua-5.1.5.tar.gz\">Download</a></td>\n </tr>\n <tr>\n <td>POCO</td>\n <td><a href=\"http://pocoproject.org/\">Official Site</a></td>\n <td><a href=\"http://downloads.sourceforge.net/project/poco/sources/poco-1.4.3/poco-1.4.3p1.zip\">Download</a></td>\n <td><a href=\"https://sourceforge.net/projects/poco/files/sources/poco-1.4.3/poco-1.4.3p1.tar.gz/download\">Download</a></td>\n </tr>\n </tbody>\n</table>\n\n<p>注意:POCO for linux 版本必须是 1.4.0 或更高,否则会引起 TCP 相关的 bug。</p>\n\n<h4 id=\"3安装\">3、安装</h4>\n\n<h5 id=\"31外部依赖的安装\">3.1、外部依赖的安装</h5>\n\n<p>Windows 下略,Linux 下基本就是:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>./configuremakesudo\n<span class=\"nv\">$ </span>make <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<h5 id=\"32安装openrtmfpcumulus\">3.2、安装 OpenRTMFP/Cumulus</h5>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">cd </span>OpenRTMFP-Cumulus/CumulusLib\n<span class=\"nv\">$ </span>make\n<span class=\"nv\">$ </span><span class=\"nb\">cd</span> ../CumulusServer\n<span class=\"nv\">$ </span>make\n</code></pre></div></div>\n\n<p>如果出现了 <code class=\"language-plaintext highlighter-rouge\">.h</code> 文件、lib 库找不到的情况,请修改 OpenRTMFP-Cumulus/CumulusLib/Makefile 或 OpenRTMFP-Cumulus/CumulusServer/Makefile。</p>\n\n<h4 id=\"4配置\">4、配置</h4>\n\n<p>通过编写 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP-Cumulus/CumulusServer/CumulusServer.ini</code> 文件来为 OpenRTMFP-Cumulus 进行个性化配置(默认是没有这个文件的),这个文件的内容形如:</p>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">;</span><span class=\"n\">CumulusServer</span><span class=\"p\">.</span><span class=\"n\">ini</span>\n<span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"mi\">1985</span>\n<span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"mi\">114688</span>\n<span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n<span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"mi\">15</span>\n<span class=\"p\">[</span><span class=\"n\">logs</span><span class=\"p\">]</span>\n<span class=\"n\">name</span><span class=\"o\">=</span><span class=\"n\">log</span>\n<span class=\"n\">directory</span><span class=\"o\">=</span><span class=\"n\">C</span><span class=\"p\">:</span><span class=\"o\">/</span><span class=\"n\">CumulusServer</span><span class=\"o\">/</span><span class=\"n\">logs</span>\n</code></pre></div></div>\n\n<p>一些字段的设置含义如下,摘自:<a href=\"https://github.com/OpenRTMFP/Cumulus/wiki/Installation\">地址</a>。</p>\n\n<ul>\n <li>公开给 Client 的端口号 <code class=\"language-plaintext highlighter-rouge\">port</code>,默认值是 1935(RTMFP 服务器的默认端口),用于 CumulusServer 监听 RTMFP 请求。</li>\n <li>UDP 缓冲区字节数 <code class=\"language-plaintext highlighter-rouge\">udpBufferSize</code>, allows to change the size in bytes of UDP reception and sending buffer. Increases this value if your operating system has a default value too lower for important loads.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">keepAliveServer</code>, time in seconds for periodically sending packets keep-alive with server, 15s by default (valid value is from 5s to 255s).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">keepAlivePeer</code>, time in seconds for periodically sending packets keep-alive between peers, 10s by default (valid value is from 5s to 255s).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.activated</code>, activate or not the edges server on the RTMFP server (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, CumulusServer stays a RTMFP server without edges ability (default value is false).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.port</code>, port for the edges server, to accept incoming new CumulusEdge instances (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, it’s the port 1936.</li>\n</ul>\n\n<blockquote>\n <p>Warning: This port will receive plain text request from edges, for this purpose it should not be made public. It’s very important for security consideration. It must be available only for CumulusEdge instances, and anything else.</p>\n</blockquote>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.attemptsBeforeFallback</code>, number of CumulusEdge attempt connections before falling back to CumulusServer (see CumulusEdge, Scalability page for more details about CumulusEdge). By default the value is 2 (in practical, 2 attempts happens after 5 sec approximately).</li>\n <li>SMTP IP 地址 <code class=\"language-plaintext highlighter-rouge\">smtp.host</code>, configure a SMTP host to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is localhost.</li>\n <li>SMTP 端口 <code class=\"language-plaintext highlighter-rouge\">smtp.port</code>, configure a SMTP port to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 25.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">smtp.timeout</code>, configure a SMTP timeout session in seconds to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 60 seconds.</li>\n <li>日志路径 <code class=\"language-plaintext highlighter-rouge\">logs.directory</code>,默认是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer/logsby</code>。</li>\n <li>日志文件名称 <code class=\"language-plaintext highlighter-rouge\">logs.name</code>,默认是<code class=\"language-plaintext highlighter-rouge\">log</code>。</li>\n</ul>\n\n<h4 id=\"5启动\">5、启动</h4>\n\n<p>Windows 下的启动方法为:</p>\n\n<pre><code class=\"language-dos\">$ CumulusServer.exe /registerService [/displayName=CumulusServer /description=\"Open Source RTMFP Server\" /startup=automatic]\n</code></pre>\n\n<p>Unix-like 下的启动方法为:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">sudo</span> ./CumulusServer <span class=\"nt\">--daemon</span> <span class=\"o\">[</span><span class=\"nt\">--pidfile</span><span class=\"o\">=</span>/var/run/CumulusServer.pid]\n</code></pre></div></div>\n\n<p>具体地,我的启动命令为:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">sudo</span> ./CumulusServer <span class=\"nt\">--daemon</span> <span class=\"nt\">--pidfile</span><span class=\"o\">=</span>./CumulusServer.pid\n</code></pre></div></div>\n\n<h4 id=\"6基本使用\">6、基本使用</h4>\n\n<p>本地 Flash client 可以通过如下语句连接:</p>\n\n<div class=\"language-as highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">$</span> <span class=\"kd\">var</span> <span class=\"nx\">nc</span><span class=\"o\">:</span><span class=\"nx\">NetConnection</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nx\">NetConnection</span><span class=\"p\">()</span><span class=\"o\">;</span><span class=\"nx\">nc</span><span class=\"p\">.</span><span class=\"nx\">connect</span><span class=\"p\">(</span><span class=\"s2\">\"rtmfp://localhost/\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>RTMFP默认是采用1935端口,如果你特别指定了其他端口,比如12345,请使用如下方式:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>nc.connect(\"rtmfp://localhost:12345/\");\n</code></pre></div></div>\n\n<h4 id=\"7扩展cumulusserverserverapplication\">7、扩展 CumulusServer(Server Application)</h4>\n\n<p>启动CumulusServer后,会在可执行文件的目录下出现一个www目录,该目录的作用,就是作为 Server Application 的默认根目录。具体的对应关系如下:</p>\n\n<blockquote>\n <p>rtmfp://host:port/ -> [CumulusServer folder]/www/main.lua (root application)</p>\n</blockquote>\n\n<blockquote>\n <p>rtmfp://host:port/myApplication -> [CumulusServer folder]/www/myApplication/main.lua</p>\n</blockquote>\n\n<blockquote>\n <p>rtmfp://host:port/Games/myGame -> [CumulusServer folder]/www/Games/myGame/main.lua</p>\n</blockquote>\n\n<p>另外要提醒的是,如果main.lua文件被修改,则不需要重启 CumulusServer,因为 Server Application 的创建是一种动态的方式。</p>\n\n<h3 id=\"三用lua编写helloworld应用扩展cumulusserver\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</h3>\n\n<p>下面的这个实例是在本地(Client 与 Server 位于同一机器上)测试的。</p>\n\n<h4 id=\"1server-side\">1、Server-side</h4>\n\n<h5 id=\"11serverconfiguration\">1.1、Server configuration</h5>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">;</span> <span class=\"n\">CumulusServer</span><span class=\"p\">.</span><span class=\"n\">ini</span>\n<span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"mi\">1935</span>\n<span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"mi\">114688</span>\n<span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n<span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"mi\">15</span>\n<span class=\"p\">[</span><span class=\"n\">logs</span><span class=\"p\">]</span><span class=\"n\">name</span> <span class=\"o\">=</span> <span class=\"n\">log</span>\n<span class=\"n\">directory</span> <span class=\"o\">=</span> <span class=\"n\">logs</span>\n</code></pre></div></div>\n\n<h5 id=\"12applicationfile\">1.2、Application file</h5>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">function</span> <span class=\"nf\">onConnection</span><span class=\"p\">(</span><span class=\"n\">client</span><span class=\"p\">,</span><span class=\"n\">response</span><span class=\"p\">,</span><span class=\"o\">...</span><span class=\"p\">)</span>\n <span class=\"k\">function</span> <span class=\"nf\">client</span><span class=\"p\">:</span><span class=\"n\">test</span><span class=\"p\">(</span><span class=\"o\">...</span><span class=\"p\">)</span>\n <span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">firstname</span> <span class=\"o\">=</span> <span class=\"n\">unpack</span><span class=\"p\">(</span><span class=\"n\">arg</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"s2\">\"Hello \"</span><span class=\"o\">..</span><span class=\"n\">firstname</span><span class=\"o\">..</span><span class=\"s2\">\" \"</span><span class=\"o\">..</span><span class=\"n\">name</span>\n <span class=\"k\">end</span>\n <span class=\"k\">end</span>\n</code></pre></div></div>\n\n<h4 id=\"2client-side\">2、Client-side</h4>\n\n<div class=\"language-java highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// CumulusClient.as</span>\n\n<span class=\"kn\">package</span> <span class=\"err\">{</span>\n <span class=\"nn\">import</span> <span class=\"n\">flash</span><span class=\"o\">.</span><span class=\"na\">display</span><span class=\"o\">.</span><span class=\"na\">Sprite</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.NetConnection</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.NetStream</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.Responder</span><span class=\"o\">;</span>\n\n <span class=\"kd\">public</span> <span class=\"kd\">class</span> <span class=\"nc\">CumulusClient</span> <span class=\"kd\">extends</span> <span class=\"nc\">Sprite</span> <span class=\"o\">{</span>\n <span class=\"kd\">private</span> <span class=\"kt\">var</span> <span class=\"nl\">nc:</span><span class=\"nc\">NetConnection</span> <span class=\"o\">=</span> <span class=\"kc\">null</span><span class=\"o\">;</span>\n \t<span class=\"kd\">private</span> <span class=\"kt\">var</span> <span class=\"nl\">ns:</span><span class=\"nc\">NetStream</span> <span class=\"o\">=</span> <span class=\"kc\">null</span><span class=\"o\">;</span>\n \t\n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">CumulusClient</span><span class=\"o\">()</span> <span class=\"o\">{</span>\n <span class=\"n\">nc</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nc\">NetConnection</span><span class=\"o\">();</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">connect</span><span class=\"o\">(</span><span class=\"s\">\"rtmfp://localhost\"</span><span class=\"o\">);</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">client</span> <span class=\"o\">=</span> <span class=\"k\">this</span><span class=\"o\">;</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">call</span><span class=\"o\">(</span><span class=\"s\">\"test\"</span><span class=\"o\">,</span><span class=\"k\">new</span> <span class=\"nc\">Responder</span><span class=\"o\">(</span><span class=\"n\">onResult</span><span class=\"o\">,</span><span class=\"n\">onStatus</span><span class=\"o\">),</span> <span class=\"s\">\"OpenRTMFP/Cumulus\"</span><span class=\"o\">,</span> <span class=\"s\">\"World\"</span><span class=\"o\">)</span>\n <span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">close</span><span class=\"o\">():</span><span class=\"kt\">void</span> <span class=\"o\">{</span> \n\t\t\t<span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">close</span><span class=\"o\">();</span>\n \t<span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">onStatus</span><span class=\"o\">(</span><span class=\"nl\">status:</span><span class=\"nc\">Object</span><span class=\"o\">):</span><span class=\"kt\">void</span> <span class=\"o\">{</span>\n \t<span class=\"n\">trace</span><span class=\"o\">(</span><span class=\"n\">status</span><span class=\"o\">.</span><span class=\"na\">description</span><span class=\"o\">)</span>\n\t <span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">onResult</span><span class=\"o\">(</span><span class=\"nl\">response:</span><span class=\"nc\">Object</span><span class=\"o\">):</span><span class=\"kt\">void</span> <span class=\"o\">{</span>\n \t<span class=\"n\">trace</span><span class=\"o\">(</span><span class=\"n\">response</span><span class=\"o\">)</span> <span class=\"c1\">// expected to display \"Hello World OpenRTMFP/Cumulus\" </span>\n\t <span class=\"o\">}</span> \n\t<span class=\"o\">}</span>\n<span class=\"o\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3运行结果\">3、运行结果</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Hello World OpenRTMFP/Cumulus\n[SWF] CumulusClient.swf - 解压缩后为 1,776 个字节\n[卸装 SWF] CumulusClient.swf\n</code></pre></div></div>\n\n<h4 id=\"4远程测试一个免费的测试服务器\">4、远程测试:一个免费的测试服务器</h4>\n\n<p>获取 Developer Key 的地址:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>http://108.59.252.39:8080/CumulusServer/index.jsp\n</code></pre></div></div>\n\n<p>服务器配置信息:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Server: amd64 OS: Linux 2.6.18-028stab095.1\nServer IP: 108.59.252.39\nOpenRTMFP as of: 22.Feb.2012\n</code></pre></div></div>\n\n<p>编写服务器段应用地址:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>http://108.59.252.39:8080/CumulusServer/manage_ssls.jsp\n</code></pre></div></div>\n\n<p>快去试试吧 :)</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"thinking":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>不要船开远了,就忘了为什么启航</title>\n \t<meta name=\"description\" content=\"2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>不要船开远了,就忘了为什么启航</h2>\t\t\n\t<time datetime=\"2022-08-11T15:53:57+00:00\" class=\"by-line\">11 Aug 2022, 杭州 | 作者 麦克船长 | 总计 3223 字</time>\n\t<div class=\"content\">\n\t\t<h3 id=\"写在前面\">写在前面</h3>\n<p>偶然翻到 2020.06.11 刚来到阿里时写的一篇内容(我是 2020 年的 6 月 4 日我入职阿里巴巴集团),是有关于来阿里的期待、对这家公司的一些粗浅初步的理解。此时再翻来看看,最大的感触就是,提醒自己勿忘初心。</p>\n\n<p>在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:</p>\n\n<ul>\n <li>很多人是带着梦想来阿里的,那么我的梦想是什么呢?</li>\n <li>最喜欢新六脉的哪句话?为什么?</li>\n <li>关于阿里企业价值观:为什么要接受这套价值观?</li>\n <li>价值观的本质意义(极度务实视角)是什么?</li>\n <li>Landing 的 SOP</li>\n <li>问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-1.png\" alt=\"image\" /></p>\n\n<p>注:2020 年平安夜 · 百年湖畔 87 期合影</p>\n\n<h3 id=\"很多人是带着梦想来阿里的那么我的梦想是什么呢\">很多人是带着梦想来阿里的,那么我的梦想是什么呢?</h3>\n\n<p>Christensen 在《创新者的窘境》中提到:每一次技术更迭,都需要破坏性创新,而破坏性创新在前一次技术更迭的胜出者内部是很难生长出来的。阿里诞生以来,不断地创造第二增长曲线:阿里巴巴、淘宝、支付宝、天猫、阿里云、钉钉 …… 这让我非常好奇。其中很多产品穿越多个时间周期,期间不断创造内生二次曲线。</p>\n\n<p>但是阿里也一样错失了很多,微信、美团、拼多多、抖/快…… 等等很多产品诞生在了其他公司,还有某些产品在不断的科技更迭中自身生长出了第二曲线。</p>\n\n<p>因此我来阿里的梦想也非常明确:<strong>参与或创造一次(甚至多次)第二曲线,可以是新产品,也可以是原有产品内生的。在这个过程中获得个人成长、个人价值。</strong></p>\n\n<p>一直以来,我有三个最想实现或得到的东西:LOVE、CREATION、FREEDOM。随着生活与工作的前行,对这三者的理解,在不断加深。在这个问题里,我想应该是讨论”CREATION”。</p>\n\n<p>CREATION 上,我的梦想的范式,大概是从自己中学时代就确立了,在某一次人类社会变革浪潮中,扮演有一定权重的角色。这里面有几个变量:<strong>什么领域(F)的变革;什么规模(S)的变革;多大的权重(W);什么角色(R)。</strong></p>\n\n<p>F 这个变量,我在中学及大学时代逐渐明确,是以相对普适的产品形式输出结果并对社会变革产生积极作用。后来越来越明确为科技与商业结合的领域。</p>\n\n<p>S、W 这两个变量,自然是越大越好。因此我会希望能够构建尽可能大的机会,或者参与到尽可能大的机会中。R 希望是有强烈 Ownership 的身份。</p>\n\n<p>因此过去几年我选择了创业。创业就像冲浪,你抓住一次浪并完成漂亮的动作,就是一次不算失败的创业。但是如果一个浪没抓住,你去追它是没意义的,而应该等待下一个浪。我认为在未来 5~10 年内难以出现规模能大到令我足够兴奋的科技浪潮。大浪潮中属于创业者的大机会很多,而中小浪潮的大机会基本只属于大平台,那么为了在壮年期做获得我的 CREATION,我选择了加入阿里这样的大平台。</p>\n\n<p>在最后做决定以及初来阿里的那个人生转折点,作为老阿里人的曲洋老师对我说的一句话,深深地鼓励了我,他说:”带着创业气质,把这里当你的舞台折腾!”</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-2.png\" alt=\"image\" /></p>\n\n<p>注:2020 年双十一 · 淘宝 KO</p>\n\n<h3 id=\"最喜欢新六脉的哪句话为什么\">最喜欢新六脉的哪句话?为什么?</h3>\n\n<p>最喜欢的是“因为信任所以简单”。</p>\n\n<p>我一直认为人最重要的两个元特质是”真实”和“谦逊”,由”真实”可以塑造自我(对内)、构建信任(对外),后者可以带来清晰的边界,继而实现人与人之间高效的互动(这种互动包括各种人际关系在内,如婚姻、合伙、共事、合作等等)。</p>\n\n<p><strong>事物(虚实皆可)呈现在人的认知中,会得到三方面的投影:facts、opinion、feeling。如果我们足够真实,当我们需要把这三方面呈现给他人时,双方就能顺畅建立信任。信任的结果,就对应到这三方面:彼此之间建立共识(facts)、求同存异(opinion)、尊重感受(feeling),这就是”简单”。</strong></p>\n\n<p>另外一句是鼓励自己勇于绽放的一条:「此次此刻,非我莫属」。</p>\n\n<p>激情、自信、积极…… 通常行为统一表现为“勇于绽放自己”,绽放有表达(语言)与投身(行为)两种表现形式。更进一步推进就是”此次此刻,非我莫属”的阿里价值观。</p>\n\n<p>低调、稳重、谦逊,其实与“此次此刻,非我莫属“,并不矛盾。这点是我来到阿里后,发现自己在过去这些年的创业中已经不知不觉改变了,从 Introvert 逐渐变成了 Extrovert 的人,而且从曾经 social 中消耗能量,逐渐变为我现在可以感知到获得能量。这种变化,是我最近来阿里才确认发生的,此前因为自己创业者的身份没有察觉这种变化的发生。</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-3.png\" alt=\"image\" /></p>\n\n<p>注:2021 年秋 · 径山之行</p>\n\n<h3 id=\"关于阿里企业价值观为什么要接受这套价值观\">关于阿里企业价值观:为什么要接受这套价值观?</h3>\n\n<p>马老师和老逍都提到这个:我们是寻找同路人,而不是教育别人。这其实非常明晰地解释了为什么阿里要构建一个毛细血管网络一样的政委体系。基于这种用人理念,政委体系不敢说是最优解,但一定是优解(而且是否有更优解的论证没有意义)。</p>\n\n<p>对于个人,我的理解是要做两件事:<strong>1)构建自己的价值观体系(初始化);2)寻找价值观契合的公司(做匹配)。这两点里,没有任何地方提到”你要改变价值观为了契合你所在的公司”。</strong></p>\n\n<p>而企业价值观呢,其实可以分两部分看待:普世价值观、独特价值观。前者因为是普世的,所以到了哪个公司这种价值观都对,这点老逍也提了,比如“客户第一”。后者是个性化的,但不存在孰高孰低,就像一个人内向还是外向,你不能说哪个是错的。</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-4.png\" alt=\"image\" /></p>\n\n<p>注:2021 年双十一 · 天天特卖团队</p>\n\n<h3 id=\"价值观的本质意义极度务实视角是什么\">价值观的本质意义(极度务实视角)是什么?</h3>\n\n<p><strong>在充分考虑价值观适配使命、愿景基础上,价值观本身的意义,在和风细雨时(即企业价值观与其他价值判断相 match 时),是看不到的。但在暴风骤雨时(即企业价值观与其他价值判断相冲突时),就能显示其实实在在的作用了。</strong>我认为包括三类,前两个是阿里整体视角,第三个是阿里内部:</p>\n\n<ul>\n <li>经济体内,阿里与其他生态位的冲突或损益关系,如曾经的美蘑口一役。</li>\n <li>经济体内,其他的生态位之间的冲突或损益关系,如曾经的十月围城。</li>\n <li>阿里人的行为价值判断,如最近的钉钉代考事件、过往的各类廉政事件。</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-5.png\" alt=\"image\" /></p>\n\n<p>注:2021 年冬 · 淘宝天猫合并前合影</p>\n\n<h3 id=\"landing的sop\">Landing 的 SOP</h3>\n\n<p>大家都说 landing 充满挑战,马老师其实给出了 landing 的 SOP 三部曲:<strong>一起打过仗、一起创过新、一起度过难。三个经历都 close 才算 smooth landing。</strong></p>\n\n<p>集团人才策略层面、HR 实操层面、Leader 层面、,对于新人 landing 能做到什么程度的保障,其实每个新人感受到的不尽相同:</p>\n\n<ul>\n <li>集团层面,始终是在构建更好的新人 landing 环境的,这符合自身价值,这能打下很好的底子。</li>\n <li>实操层面,包括面试阶段对候选人的价值观判断、预期管理,面试及入职后公司文化及人才体系的事实呈现、内化吸收和长期解惑。</li>\n <li>Leader 层面,这是新人体感最强烈的部分,也是最重要的部分。尽管拥抱变化,但首先 Leader 需要给出尽可能最全面的考虑,其次是对候选人的预期管理。好的 Leader 会给候选人提供合理的着陆点、多个降落伞、缓冲垫,完成 smooth landing。</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-6.png\" alt=\"image\" /></p>\n\n<p>注:2021 年夏 · 出差厦漳泉</p>\n\n<h3 id=\"问问自己来到阿里如果初期我可能需要做一点改变那会是什么\">问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</h3>\n\n<p>曾经个人的激情与动力,常来自于“增长”。常说<strong>高增长掩盖一切</strong>,所以未来在阿里如果不能如创业般快速获得反馈得到积极结果,并且大平台中必然要接受大量关联方共同参与项目而导致的效率降低,因此我要逐渐改变自己,重新适应这种环境下的激情与动力获得方式。</p>\n\n<p>另一方面,作为创业公司的负责人,工作中鲜有因为内部原因而无法推进的事情,但是扮演肩部或腰部角色时,需要接受头部决策的一定程度不可控,这是我需要作出的适应与改变。关于这一点,我在几个月前就已经在做预期管理和心态调整,我认为以创业者的强适应性,这可能并不会是问题,但是我习惯于保持谨慎的乐观来面对自己。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</title>\n \t<meta name=\"description\" content=\"阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</h2>\t\t\n\t<time datetime=\"2021-11-11T19:59:43+00:00\" class=\"by-line\">11 Nov 2021, 杭州 | 作者 麦克船长 | 总计 917 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2021-11-11-captain-tttm-1.jpg\" alt=\"imagee\" /></p>\n\n<h3 id=\"天天特卖团队理念\">天天特卖团队理念</h3>\n\n<h4 id=\"特卖合伙人\">特卖合伙人</h4>\n\n<p>以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。特卖核心管理团队每 Q 会进行一次班子建设通晒。</p>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-9.jpg\" alt=\"imagee\" /></p>\n\n<h4 id=\"如何理解协作\">如何理解协作?</h4>\n\n<p>从长时间线来看,我们是为了不断积累信用,像一张信用卡一样,不断获得别人愿意支持我们的更大额度。不要事情结果还可以,而我们却没有积累到信用。互联网本质也是现代工业。而现代工业,一是社会分工,二是社会协作。想取得现代工业项目的结果,就要有更大的人才包容度、环境包容度。工作的结果就是在妥协与博弈中取得的,这是和光同尘的本质,也是现代工业复杂系统拿到结果的本质。只有这样我们才能让越来越多的人追随我们一起 do something,这种追随不一定只有上下级才是,而是愿意并且相信和我们能到达更远的地方。这背后的信用,要我们一步一个脚印地去积累,对他人给予的信任要保持敬畏、如履薄冰、懂得感恩。对每一段阶段性或长或短结束的合作,都要表达感谢。</p>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-8.jpg\" alt=\"imagee\" /></p>\n\n<h4 id=\"如何看待同学的优势及短板\">如何看待同学的优势及短板?</h4>\n\n<ul>\n <li>优势:讲优势有两个可能的目的,要么组织会在未来任务分配上重点考虑发挥该同学优势的事情,要告诉 TA,要激励 TA,是 TA 前行的自信来源之一。要么是对于同学也把握不准的特点,我们明确告诉 TA 这是你被我欣赏的优点。</li>\n <li>短板:什么是要讲的短板?未来一段时间,最期待你补足提升的。一旦这方面显著进步,就会向上迈进很大一步,甚至可以突破自己当下成长的瓶颈。要花多少篇幅讲?要比优势,有更大篇幅去讲。讲完就结束了么?对这个短板,一定要表达态度,也一定要对是否有方法、什么方法来补足短板要和同学沟通。</li>\n <li>无论是优势,还是短板,要说到点儿上,不要说片儿汤话。要让同学们能够引起思考、启发的。</li>\n</ul>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-10.jpg\" alt=\"imagee\" /></p>\n\n<h3 id=\"天天特卖期待你的加入\">天天特卖期待你的加入!</h3>\n\n<p>新天天特卖缘起于「手淘下沉市场战役)」,于 2021 年初上线,以「极致性价比货源、裸价直降、全网比价、买贵必赔」打造手淘极致价格敏感人群的购物阵地。目前天天特卖团队有行业运营、用户运营、数据策略、整合营销、直播运营、内容运营等岗位,有兴趣的同学可以钉钉随时找我,期待你的加入!</p>\n\n<h4 id=\"欢迎添加我的微信sinosuperman-推荐自荐--\">欢迎添加我的微信:sinosuperman 推荐、自荐 ^ ^</h4>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-11.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-2.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-3.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-4.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-5.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-6.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-7.jpg\" alt=\"imagee\" /></p>\n\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的阿里一年香(入职阿里一周年)</title>\n \t<meta name=\"description\" content=\"本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的阿里一年香(入职阿里一周年)</h2>\t\t\n\t<time datetime=\"2021-06-04T15:42:43+00:00\" class=\"by-line\">04 Jun 2021, 杭州 | 作者 麦克船长 | 总计 304 字</time>\n\t<div class=\"content\">\n\t\t<p>To 钟超</p>\n\n<p>1 周年快乐!很开心我们有这样一段共事的机会,虽开始时有些许波折,但随着进一步相处,我们很快能做到彼此欣赏、英雄相惜、默契配合,也特别感谢你对我的信任和支持,这是一切共事的基础。你强大的自驱力、脑力、对新事物的理解学习能力,都是最近几手新人里比较突出的。特别钦佩于你的执着和初性,对一件事认定后,迸发出的强大战斗力和决心。今天特卖这个新业务需要扎下根基,还真的需要一些舍我其谁的胆魄和更为犀利的突破,我也相信「新特卖」能成为你在阿里又一代表作,我希望我们的团队能为之骄傲和自豪,我们能不负公司所托,真正在下沉市场这场硬仗上有所建树,井取得令我们自己感到骄傲的突破,一起加油。</p>\n\n<p>From 麦克船长的主管</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>担任淘宝产品总负责人的双十一,是怎样的体验?</title>\n \t<meta name=\"description\" content=\"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>担任淘宝产品总负责人的双十一,是怎样的体验?</h2>\t\t\n\t<time datetime=\"2020-11-11T15:59:43+00:00\" class=\"by-line\">11 Nov 2020, 杭州 | 作者 麦克船长 | 总计 138 字</time>\n\t<div class=\"content\">\n\t\t<p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\n\n<p><img src=\"/img/src/2020-11-11-captain-double-eleven-1.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-2.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-3.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-4.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-5.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-6.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-7.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-8.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-9.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-10.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-11.jpg\" alt=\"image\" /></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>疫后怎么做餐饮品牌?三叉戟模式或成标配</title>\n \t<meta name=\"description\" content=\"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>疫后怎么做餐饮品牌?三叉戟模式或成标配</h2>\t\t\n\t<time datetime=\"2020-04-14T16:42:43+00:00\" class=\"by-line\">14 Apr 2020, 杭州 | 作者 麦克船长 | 总计 1845 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><img src=\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\n\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\n\n<ul>\n <li>外卖</li>\n <li>做饭</li>\n <li>速食</li>\n</ul>\n\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\n\n<h3 id=\"标品化外卖业务\">标品化外卖业务</h3>\n\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\n\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\n\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\n\n<ul>\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\n</ul>\n\n<h3 id=\"社区生鲜前置仓\">社区生鲜前置仓</h3>\n\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\n\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\n\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\n\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\n\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\n\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\n\n<ul>\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\n</ul>\n\n<h3 id=\"线上预包装食品\">线上预包装食品</h3>\n\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\n\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\n\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\n\n<ul>\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\n</ul>\n\n<h3 id=\"线下重构新餐饮时代到来\">线下重构,新餐饮时代到来</h3>\n\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\n\n<ul>\n <li>用「标品化外卖」覆盖外卖场景</li>\n <li>用「生鲜前置仓」覆盖做饭场景</li>\n <li>用「预包装食品」覆盖速食场景</li>\n</ul>\n\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\n\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\n\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>延迟满足,才有自由</title>\n \t<meta name=\"description\" content=\"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>延迟满足,才有自由</h2>\t\t\n\t<time datetime=\"2020-04-11T06:18:03+00:00\" class=\"by-line\">11 Apr 2020, 杭州 | 作者 麦克船长 | 总计 4478 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\n\n<h3 id=\"1儿童时期的延迟满足\">1、儿童时期的延迟满足</h3>\n\n<h4 id=\"棉花糖实验\">棉花糖实验</h4>\n\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\n\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\n\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\n\n<p>先讲两个我自己的例子吧。</p>\n\n<h4 id=\"黑加仑零食\">黑加仑零食</h4>\n\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\n\n<h4 id=\"暑假作业\">暑假作业</h4>\n\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\n\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\n\n<h4 id=\"延迟满足的背后是意志力自控力\">延迟满足的背后,是意志力/自控力</h4>\n\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\n\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\n\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\n\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\n\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\n\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\n\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\n\n<h3 id=\"2快乐理论挑战延迟满足\">2、快乐理论,挑战延迟满足</h3>\n\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\n\n<h4 id=\"烂苹果\">烂苹果</h4>\n\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\n\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\n\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\n\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\n\n<h4 id=\"快乐理论\">快乐理论</h4>\n\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\n\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\n\n<p>但这个理论角度对吗?</p>\n\n<h3 id=\"3我们应该在哪个范畴内讨论延迟满足\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\n\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\n\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\n\n<h4 id=\"仅有正反馈刺激的奶头乐\">仅有正反馈刺激的奶头乐</h4>\n\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\n\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\n\n<h4 id=\"延迟满足重在顺序而非时间\">延迟满足重在顺序,而非时间</h4>\n\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\n\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\n\n<h3 id=\"4常见的延迟满足与即时满足\">4、常见的延迟满足与即时满足</h3>\n\n<h4 id=\"初阶对手vs浅度延迟满足\">初阶对手 vs 浅度延迟满足</h4>\n\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\n\n<ul>\n <li>\n <p>睡懒觉</p>\n </li>\n <li>\n <p>过量饮食</p>\n </li>\n <li>\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\n </li>\n <li>\n <p>冲动情绪</p>\n </li>\n <li>\n <p>炫耀(粗俗说就是装B)</p>\n </li>\n</ul>\n\n<p>……</p>\n\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\n\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\n\n<h4 id=\"中阶对手vs中度延迟满足\">中阶对手 vs 中度延迟满足</h4>\n\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\n\n<ul>\n <li>\n <p>在家边看电视/视频,边学习/工作</p>\n </li>\n <li>\n <p>依赖二手知识,而怠于一手知识</p>\n </li>\n <li>\n <p>刷知乎、行业资讯 APP</p>\n </li>\n</ul>\n\n<p>……</p>\n\n<p>例子会有很多,我们仅稍微说下这三个。</p>\n\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\n\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\n\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\n\n<h4 id=\"高阶对手vs深度延迟满足\">高阶对手 vs 深度延迟满足</h4>\n\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\n\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\n\n<p>我这里就说一个影响很大的:</p>\n\n<p>* 急于行动</p>\n\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\n\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\n\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\n\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\n\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\n\n<h3 id=\"5延迟满足的驻留时长\">5、延迟满足的驻留时长</h3>\n\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\n\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\n\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\n\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\n\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\n\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\n\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\n\n<h3 id=\"后记\">后记</h3>\n\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\n\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>未来人工智能就是要:让普通人过上现在富豪们的生活</title>\n \t<meta name=\"description\" content=\"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>未来人工智能就是要:让普通人过上现在富豪们的生活</h2>\t\t\n\t<time datetime=\"2017-02-23T18:23:33+00:00\" class=\"by-line\">23 Feb 2017, 北京 | 作者 麦克船长 | 总计 627 字</time>\n\t<div class=\"content\">\n\t\t<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\n\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\n\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\n\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\n\n<h4 id=\"但是我没有钱呢\">但是我没有钱呢?</h4>\n\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\n\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\n\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\n\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\n\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\n\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\n\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>我们是应该「断舍离」还是「念念不忘,必有回响」</title>\n \t<meta name=\"description\" content=\"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>我们是应该「断舍离」还是「念念不忘,必有回响」</h2>\t\t\n\t<time datetime=\"2017-01-31T20:59:21+00:00\" class=\"by-line\">31 Jan 2017, 北京 | 作者 麦克船长 | 总计 2577 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"引子\">引子</h3>\n\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\n\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\n\n<p>「嗨,我说老朱,讲讲你啊!」</p>\n\n<p>「我就不提啦。」</p>\n\n<p>「为什么啊?说说,说说!」</p>\n\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\n\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\n\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\n\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\n\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\n\n<blockquote>\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\n</blockquote>\n\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\n\n<h3 id=\"断舍离\">断舍离</h3>\n\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\n\n<p><img src=\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\" alt=\"image\" /></p>\n\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\n\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\n\n<h3 id=\"念念不忘必有回响\">念念不忘,必有回响</h3>\n\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\n\n<p><img src=\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\" alt=\"image\" /></p>\n\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\n\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\n\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\n\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\n\n<h3 id=\"用正负反馈来判断何时何为\">用「正负反馈」来判断何时何为?</h3>\n\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\n\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\n\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\n\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\n\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\n\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\n\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\n\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\n\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\n\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\n\n<h3 id=\"后记\">后记</h3>\n\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\n\n<p>然后,我们一起等待,那声回响。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>一名出色软件工程师的技术基本功:编程与工具</title>\n \t<meta name=\"description\" content=\"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>一名出色软件工程师的技术基本功:编程与工具</h2>\t\t\n\t<time datetime=\"2012-05-15T17:06:59+00:00\" class=\"by-line\">15 May 2012, 广州 | 作者 麦克船长 | 总计 2132 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"0写在前面\">0、写在前面</h3>\n\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\n\n<h3 id=\"1编程\">1、编程</h3>\n\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\n\n<h4 id=\"如果你精通c语言\">如果你精通 C 语言</h4>\n\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\n\n<h4 id=\"如果你精通c语言-1\">如果你精通 C++ 语言</h4>\n\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\n\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\n\n<h4 id=\"精通的关键还是针对语言核心来说的\">精通的关键,还是针对语言核心来说的。</h4>\n\n<p>第一,你要对这个语言的语法特性熟稔;</p>\n\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\n\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\n\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\n\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\n\n<h4 id=\"与计算机网络的基础结构相关联的技术实现\">与计算机、网络的基础结构相关联的技术实现</h4>\n\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\n\n<h3 id=\"2工具\">2、工具</h3>\n\n<p>对于工具有三个层面:</p>\n\n<p>第一,是熟练的使用一些工具。</p>\n\n<p>第二,是能够发现提高生产力的工具。</p>\n\n<p>第三,是能够在无可用工具时自己编写工具。</p>\n\n<p>那么都有哪些最最最基本的工具呢?</p>\n\n<h4 id=\"ideintegrateddevelopmentenvironment\">IDE(Integrated Development Environment)</h4>\n\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\n\n<h4 id=\"vcsversioncontrolsystem\">VCS(Version Control System)</h4>\n\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\n\n<blockquote>\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black & white.</p>\n</blockquote>\n\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\n\n<h4 id=\"clicommandlineinterface\">CLI(Command Line Interface)</h4>\n\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\n\n<h3 id=\"3结语\">3、结语</h3>\n\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"web":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的 Jekyll 快速教程</title>\n \t<meta name=\"description\" content=\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的 Jekyll 快速教程</h2>\t\t\n\t<time datetime=\"2021-12-23T19:43:02+00:00\" class=\"by-line\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\n\n<h3 id=\"part-1基本特点\">Part 1、基本特点</h3>\n\n<h4 id=\"一基本语法\">一、基本语法</h4>\n\n<ul>\n <li>变量:用双大括号表示变量 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code></li>\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\n <li>支持循环与分支结构:比如 <code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 和 <code class=\"language-plaintext highlighter-rouge\">if-elsif-else-endif</code> :可以使用 <code class=\"language-plaintext highlighter-rouge\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\n</ul>\n\n<h4 id=\"二典型-jekyll-项目结构及重要文件介绍\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\n\n<h5 id=\"1配置文件-_configyml\">1、配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code></h5>\n\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\"language-plaintext highlighter-rouge\">encoding: utf-8</code> 这一条。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Site settings</span>\n<span class=\"na\">encoding</span><span class=\"pi\">:</span> <span class=\"s\">utf-8</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">麦克船长的技术、产品与商业博客</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\"</span>\n<span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptain.com\"</span>\n<span class=\"na\">author</span><span class=\"pi\">:</span>\n <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Your</span><span class=\"nv\"> </span><span class=\"s\">Name\"</span>\n <span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptian.com\"</span>\n</code></pre></div></div>\n\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Markdown and highlighter</span>\n<span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Plugins</span>\n<span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-paginate</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-sitemap</span>\n</code></pre></div></div>\n\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Build settings</span>\n<span class=\"na\">baseurl</span><span class=\"pi\">:</span> <span class=\"c1\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\n<span class=\"na\">source</span><span class=\"pi\">:</span> <span class=\"s\">.</span>\n<span class=\"na\">destination</span><span class=\"pi\">:</span> <span class=\"s\">./_site</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:title</span>\n<span class=\"na\">paginate</span><span class=\"pi\">:</span> <span class=\"m\">20</span>\n<span class=\"na\">paginate_path</span><span class=\"pi\">:</span> <span class=\"s\">/page:num/</span>\n<span class=\"na\">collections</span><span class=\"pi\">:</span>\n <span class=\"na\">posts</span><span class=\"pi\">:</span>\n <span class=\"na\">output</span><span class=\"pi\">:</span> <span class=\"no\">true</span>\n <span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:year/:month/:day/:title/</span>\n <span class=\"na\">directory</span><span class=\"pi\">:</span> <span class=\"s\">_posts</span>\n</code></pre></div></div>\n\n<h5 id=\"2布局文件_layouts-目录下的文件规则\">2、布局文件:<code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的文件规则</h5>\n\n<p>Jekyll 的 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\"language-plaintext highlighter-rouge\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\"language-plaintext highlighter-rouge\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\n\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\"language-plaintext highlighter-rouge\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\n\n<h5 id=\"3页面文件及其头部\">3、页面文件及其头部</h5>\n\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">page</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/categories/</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">Categories</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>每个页面文件的头部都会有layout,并与 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的某个文件对应。</p>\n\n<h3 id=\"part-2jekyll-中的全局变量\">Part 2、Jekyll 中的全局变量</h3>\n\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:包含当前页面或文章的内容。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:包含有关网站的所有标签的信息。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\n</ul>\n\n<p>这些变量可以在模板中使用,比如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><h1></span>{{ page.title }}<span class=\"nt\"></h1></span>\n<span class=\"nt\"><p></span>{{ site.description }}<span class=\"nt\"></p></span>\n<span class=\"nt\"><ul></span>\n {{ for category in site.categories %}\n <span class=\"nt\"><li></span>{{ category }}<span class=\"nt\"></li></span>\n {{ endfor %}\n<span class=\"nt\"></ul></span> \n</code></pre></div></div>\n\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">my_custom_variable</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Hello</span><span class=\"nv\"> </span><span class=\"s\">World\"</span>\n</code></pre></div></div>\n\n<p>然后就可以在模板文件中使用 <code class=\"language-plaintext highlighter-rouge\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\n\n<h4 id=\"一site变量\">一、site变量</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\n\n<h5 id=\"1sitecategories\">1、<code class=\"language-plaintext highlighter-rouge\">site.categories</code></h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\"language-plaintext highlighter-rouge\">first</code> 就是 <code class=\"language-plaintext highlighter-rouge\">category</code> 的名字,如下使用:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>\n</code></pre></div></div>\n\n<h5 id=\"2sitepages\">2、site.pages</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\"language-plaintext highlighter-rouge\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3其他常用属性\">3、其他常用属性</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site.title</code>:是网站的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.related_posts</code>:相关文章的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.data</code>:从 <code class=\"language-plaintext highlighter-rouge\">_data</code> 目录加载的数据。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.static_files</code>:静态文件的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.collections</code>:自定义集合的列表。</li>\n</ul>\n\n<h4 id=\"二page-变量\">二、<code class=\"language-plaintext highlighter-rouge\">page</code> 变量</h4>\n\n<p>在 Jekyll 中,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量属性包括:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">layout</code>:表示页面使用的布局模板的名称。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">title</code>:表示页面的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">date</code>:表示页面的发布日期。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">categories</code>:表示页面所属的分类列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:表示页面所属的标签列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\n</ul>\n\n<p>在模板中,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.属性名 }}</code> 的方式来访问 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.title }}</code>。此外,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量还有其他属性,如 <code class=\"language-plaintext highlighter-rouge\">permalink</code>、<code class=\"language-plaintext highlighter-rouge\">excerpt</code>、<code class=\"language-plaintext highlighter-rouge\">url</code> 等,可以根据需要调用。</p>\n\n<h3 id=\"part-3控制结构\">Part 3、控制结构</h3>\n\n<h4 id=\"1if-else-分支结构\">1、<code class=\"language-plaintext highlighter-rouge\">if-else</code> 分支结构</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ if tmp_var == \"type1\" %}\n{{ elsif tmp_var == \"type2\" %}\n{{ elsif tmp_var == \"type3\" %}\n{{ elsif tmp_var == \"type4\" %}\n{{ else tmp_var == \"type5\" %}\n{{ endif %}\n</code></pre></div></div>\n\n<h4 id=\"2for-endfor-循环结构\">2、<code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 循环结构</h4>\n\n<p>不带条件判断的 <code class=\"language-plaintext highlighter-rouge\">for</code> 循环如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for post in paginator.posts %}\n\t<span class=\"c\"><!-- Your other sentences --></span>\n{{ endfor %}\n</code></pre></div></div>\n\n<p>带条件循环的 <code class=\"language-plaintext highlighter-rouge\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages | where: \"dir\", \"categories\" %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3jekyll-支持的其他结构包括\">3、Jekyll 支持的其他结构包括:</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">capture</code> 用于捕获输出的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">cycle</code> 用于循环一组字符串的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">include</code> 用于包含其他文件的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">while</code> 用于在满足指定条件时执行代码的结构。</li>\n</ul>\n\n<h3 id=\"参考\">参考:</h3>\n\n<p>1、<a href=\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\n2、<a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\n \t<meta name=\"description\" content=\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\t\t\n\t<time datetime=\"2021-12-21T15:53:57+00:00\" class=\"by-line\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#写在前面\" id=\"markdown-toc-写在前面\">写在前面</a></li>\n <li><a href=\"#1github上的准备\" id=\"markdown-toc-1github上的准备\">1、GitHub 上的准备</a></li>\n <li><a href=\"#2了解ruby和jekyll\" id=\"markdown-toc-2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</a></li>\n <li><a href=\"#3了解gem\" id=\"markdown-toc-3了解gem\">3、了解 Gem</a></li>\n <li><a href=\"#4安装homebrew\" id=\"markdown-toc-4安装homebrew\">4、安装 Homebrew</a></li>\n <li><a href=\"#5用homebrew安装ruby\" id=\"markdown-toc-5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</a></li>\n <li><a href=\"#6安装jekyll和bundler\" id=\"markdown-toc-6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</a></li>\n <li><a href=\"#7使用bundle管理包依赖关系\" id=\"markdown-toc-7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</a></li>\n <li><a href=\"#8本地启动一下看看\" id=\"markdown-toc-8本地启动一下看看\">8、本地启动一下看看</a></li>\n <li><a href=\"#9用jekyll创建一个项目\" id=\"markdown-toc-9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</a></li>\n <li><a href=\"#10修改gemfile文件\" id=\"markdown-toc-10修改gemfile文件\">10、修改 Gemfile 文件</a></li>\n <li><a href=\"#11配置githubpages\" id=\"markdown-toc-11配置githubpages\">11、配置 Github Pages</a></li>\n <li><a href=\"#12配置一个jekylltheme\" id=\"markdown-toc-12配置一个jekylltheme\">12、配置一个 Jekyll Theme</a></li>\n <li><a href=\"#13设置自定义域名\" id=\"markdown-toc-13设置自定义域名\">13、设置自定义域名</a></li>\n <li><a href=\"#14用-rouge-实现代码高亮\" id=\"markdown-toc-14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</a></li>\n <li><a href=\"#15一些扩展问题\" id=\"markdown-toc-15一些扩展问题\">15、一些扩展问题</a> <ul>\n <li><a href=\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\" id=\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\n <li><a href=\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\" id=\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\n <li><a href=\"#q3如何为每篇文章添加一个目录\" id=\"markdown-toc-q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</a></li>\n <li><a href=\"#q4如何在-jekyll-中支持-katex\" id=\"markdown-toc-q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\n <li><a href=\"#在-githubio-上\" id=\"markdown-toc-在-githubio-上\">在 GitHub.io 上</a></li>\n <li><a href=\"#如果不在-githubio-上则还需要额外工作\" id=\"markdown-toc-如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\n <li><a href=\"#使用示例\" id=\"markdown-toc-使用示例\">使用示例</a></li>\n </ul>\n </li>\n <li><a href=\"#q5jekyll-中如何支持-graphviz-\" id=\"markdown-toc-q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\n <li><a href=\"#q6如何显示--或者--\" id=\"markdown-toc-q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</a></li>\n </ul>\n </li>\n <li><a href=\"#参考\" id=\"markdown-toc-参考\">参考</a></li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\n\n<ul>\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\n</ul>\n\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\n\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\n\n<h3 id=\"1github上的准备\">1、GitHub 上的准备</h3>\n\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git clone https://github.com/username/username.github.io\n</code></pre></div></div>\n\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git add <span class=\"nt\">--all</span>\n<span class=\"nv\">$ </span>git commit <span class=\"nt\">-m</span> <span class=\"s2\">\"Initial commit\"</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</h3>\n\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\n\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\n\n<h3 id=\"3了解gem\">3、了解 Gem</h3>\n\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\n\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\ngem sources -l\n</code></pre></div></div>\n\n<h3 id=\"4安装homebrew\">4、安装 Homebrew</h3>\n\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>/bin/bash <span class=\"nt\">-c</span> <span class=\"s2\">\"</span><span class=\"si\">$(</span>curl <span class=\"nt\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\"si\">)</span><span class=\"s2\">\"</span>\n</code></pre></div></div>\n\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\n\n<h3 id=\"5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</h3>\n\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>brew <span class=\"nb\">install </span>chruby ruby-install xz\n</code></pre></div></div>\n\n<p>安装 Ruby 的最新版本:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby-install ruby\n</code></pre></div></div>\n\n<p>这时候提示如下问题:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"o\">>>></span> Updating ruby versions ...\n<span class=\"o\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\"se\">\\</span>\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\n<span class=\"o\">!!!</span> Failed to download ruby versions!\n</code></pre></div></div>\n\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ echo \"185.199.111.133 raw.githubusercontent.com\" >> /etc/hosts\n</code></pre></div></div>\n\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/chruby.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/auto.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"chruby ruby-3.1.2\"</span> <span class=\"o\">>></span> ~/.zshrc <span class=\"c\"># run 'chruby' to see actual version</span>\n</code></pre></div></div>\n\n<p>再看下 Ruby 版本对不对:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby <span class=\"nt\">-v</span>\n</code></pre></div></div>\n\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\n\n<h3 id=\"6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"nb\">install </span>jekyll bundler\n</code></pre></div></div>\n\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\n\n<h3 id=\"7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</h3>\n\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">source</span> <span class=\"s1\">'https://rubygems.org'</span>\ngem <span class=\"s1\">'nokogiri'</span>\ngem <span class=\"s1\">'rack'</span>, <span class=\"s1\">'~> 2.2.4'</span>\ngem <span class=\"s1\">'rspec'</span>\ngem <span class=\"s1\">'jekyll'</span>\n</code></pre></div></div>\n\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ git add Gemfile Gemfile.lock\n</code></pre></div></div>\n\n<h3 id=\"8本地启动一下看看\">8、本地启动一下看看</h3>\n\n<p>先用 bundle 如下命令来启动:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: none\n Source: /Users/captain/Workspace/poechant.github.io\n Destination: /Users/captain/Workspace/poechant.github.io/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n done in 0.014 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\n Server address: http://127.0.0.1:4000\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\" alt=\"image\" /></p>\n\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git pull <span class=\"nt\">--no-rebase</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll new CaptainMikeBlog\n<span class=\"nv\">$ </span><span class=\"nb\">cd </span>CaptainMikeBlog\n<span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n Jekyll Feed: Generating feed for posts\n done in 0.365 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\n Server address: http://127.0.0.1:4000/\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\" alt=\"image\" /></p>\n\n<h3 id=\"10修改gemfile文件\">10、修改 Gemfile 文件</h3>\n\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"s2\">\"github-pages\"</span>, <span class=\"s2\">\"~> GITHUB-PAGES-VERSION\"</span>, group: :jekyll_plugins\n</code></pre></div></div>\n\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ bundle install\n</code></pre></div></div>\n\n<p>再本地启动服务器测试:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>得到如下提示:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\nPrepending <span class=\"sb\">`</span>bundle <span class=\"nb\">exec</span><span class=\"sb\">`</span> to your <span class=\"nb\">command </span>may solve this. <span class=\"o\">(</span>Gem::LoadError<span class=\"o\">)</span>\n</code></pre></div></div>\n\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle add webrick\n<span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\n\n<h3 id=\"11配置githubpages\">11、配置 Github Pages</h3>\n\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>baseurl: \"\"\nurl: \"http://your-username.github.io\"\n</code></pre></div></div>\n\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\n\n<h3 id=\"12配置一个jekylltheme\">12、配置一个 Jekyll Theme</h3>\n\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_includes\n_layouts\n_sass\ncss\njs\nimg\n404.markdown\nindex.html\n</code></pre></div></div>\n\n<p>不同主题会有所不同,这里只列个大概。</p>\n\n<h3 id=\"13设置自定义域名\">13、设置自定义域名</h3>\n\n<p>添加四条 A 记录,记录值如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>185.199.108.153\n185.199.109.153\n185.199.110.153\n185.199.111.153\n</code></pre></div></div>\n\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\n\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\n\n<h3 id=\"14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</h3>\n\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem install kramdom rouge\n</code></pre></div></div>\n\n<p>然后配置 _config.yml 文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>然后用 rouge 创建 syntax.css 文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>rougify style github <span class=\"o\">></span> css/syntax.css\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">_include/head.html</code> 文件中添加:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span> <span class=\"na\">href=</span><span class=\"s\">\"/css/syntax.css\"</span> <span class=\"nt\">/></span>\n</code></pre></div></div>\n\n<h3 id=\"15一些扩展问题\">15、一些扩展问题</h3>\n\n<h4 id=\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\n\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">这是一篇文章</span>\n<span class=\"na\">excerpt</span><span class=\"pi\">:</span> <span class=\"s\">这是文章的摘要</span>\n<span class=\"nn\">---</span>\n\n这是文章的正文内容\n</code></pre></div></div>\n\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><ul></span>\n {% for post in paginator.posts %}\n <span class=\"nt\"><li></span>\n <span class=\"nt\"><h2><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{{ post.url }}\"</span><span class=\"nt\">></span>{{ post.title }}<span class=\"nt\"></a></h2></span>\n <span class=\"nt\"><p></span>{{ post.excerpt }}<span class=\"nt\"></p></span>\n <span class=\"nt\"></li></span>\n {% endfor %}\n<span class=\"nt\"></ul></span>\n</code></pre></div></div>\n\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\n\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\n\n<h4 id=\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\n\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\"language-plaintext highlighter-rouge\">_layouts</code>目录下创建一个<code class=\"language-plaintext highlighter-rouge\">category.html</code>文件:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>---\nlayout: default\n---\n\n<span class=\"nt\"><div</span> <span class=\"na\">class=</span><span class=\"s\">\"container\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><br></span>\n {% if site.categories[page.category] %}\n {% for post in site.categories[page.category] %}\n <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{% if site.baseurl == \"</span><span class=\"err\">/\"</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">}}{%</span> <span class=\"na\">else</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">|</span> <span class=\"na\">prepend:</span> <span class=\"na\">site.baseurl</span> <span class=\"err\">}}{%</span> <span class=\"na\">endif</span> <span class=\"err\">%}\"</span><span class=\"nt\">></span>\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\n <span class=\"nt\"></a></span>\n {% endfor %}\n {% else %}\n <span class=\"nt\"><br></span>\n <span class=\"nt\"><p></span>No posts for this category. If you have something in mind, check <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"/write\"</span><span class=\"nt\">></span>Write For Us<span class=\"nt\"></a></span>page.<span class=\"nt\"></p></span>\n {% endif %}\n<span class=\"nt\"></div></span>\n</code></pre></div></div>\n\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\"language-plaintext highlighter-rouge\">_config.yml</code>文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">include</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"s1\">'</span><span class=\"s\">_categories'</span><span class=\"pi\">]</span>\n</code></pre></div></div>\n\n<p>在根目录创建一个<code class=\"language-plaintext highlighter-rouge\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\"language-plaintext highlighter-rouge\">ai.html</code>如下:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">category</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">人工智能</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s\">This is the description.</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/category/ai</span>\n<span class=\"na\">category</span><span class=\"pi\">:</span> <span class=\"s\">ai</span>\n<span class=\"na\">category_type</span><span class=\"pi\">:</span> <span class=\"s\">tech</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>那么之后每次创建文件时,在头部写<code class=\"language-plaintext highlighter-rouge\">category</code>一定要与这些<code class=\"language-plaintext highlighter-rouge\">categories</code>中的<code class=\"language-plaintext highlighter-rouge\">html</code>文件对应起来。</p>\n\n<h4 id=\"q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</h4>\n\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">*</span> TOC\n{:toc}\n</code></pre></div></div>\n\n<h4 id=\"q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\n\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\n\n<h5 id=\"在-githubio-上\">在 GitHub.io 上</h5>\n\n<p>先修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code>:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">math_engine</span><span class=\"pi\">:</span> <span class=\"s\">katex</span>\n</code></pre></div></div>\n\n<p>然后修改 <code class=\"language-plaintext highlighter-rouge\">_includes/head.html</code> 文件,在 <code class=\"language-plaintext highlighter-rouge\"><head></code> 与 <code class=\"language-plaintext highlighter-rouge\"></head></code> 中间:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"><!--KaTeX--></span>\n <span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span>\n <span class=\"na\">href=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script></span>\n <span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">addEventListener</span><span class=\"p\">(</span><span class=\"dl\">\"</span><span class=\"s2\">DOMContentLoaded</span><span class=\"dl\">\"</span><span class=\"p\">,</span> <span class=\"kd\">function</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"nx\">renderMathInElement</span><span class=\"p\">(</span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">body</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n <span class=\"c1\">// ...options...</span>\n <span class=\"p\">});</span>\n <span class=\"p\">});</span>\n <span class=\"nt\"></script></span>\n</code></pre></div></div>\n\n<h5 id=\"如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</h5>\n\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem <span class=\"nb\">install </span>kramdom-math-katex\n\ngem <span class=\"nb\">install </span>katex\ngem <span class=\"nb\">install </span>execjs\n\ngem <span class=\"nb\">install </span>therubyracer\ngem <span class=\"nb\">install </span>therubyrhino\ngem <span class=\"nb\">install </span>duktape\n</code></pre></div></div>\n\n<h5 id=\"使用示例\">使用示例</h5>\n\n<p>以如下方式输入输入如下内容:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}\n$$ \\sum_{i=1}^{n} a_i $$\n{% endraw %}\n</code></pre></div></div>\n\n<p>就会得到一个数学公式:</p>\n\n\\[\\sum_{i=1}^{n} a_i\\]\n\n<h4 id=\"q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\n\n<p>这要依赖 <code class=\"language-plaintext highlighter-rouge\">jekyll-graphviz-dot</code>,修改 <code class=\"language-plaintext highlighter-rouge\">Gemfile</code> 增加一句:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>group :jekyll_plugins <span class=\"k\">do\n </span>gem <span class=\"s2\">\"jekyll-graphviz-dot\"</span>\nend\n</code></pre></div></div>\n\n<p>再修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 配置文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-graphviz</span>\n</code></pre></div></div>\n\n<p>再在本地安装 graphviz,可以通过 <code class=\"language-plaintext highlighter-rouge\">conda install graphviz</code> 或者 <code class=\"language-plaintext highlighter-rouge\">brew install graphviz</code>。然后 <code class=\"language-plaintext highlighter-rouge\">bundle install</code> 再 <code class=\"language-plaintext highlighter-rouge\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\n\n<pre><code class=\"language-graphviz\">{% graph some graph title %}\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n{% endgraph %}\n</code></pre>\n\n<p>如果看到如下效果,就说明你都配置成功了:</p>\n\n<div class=\"graphviz-wrapper\">\n\n<!-- Generated by graphviz version 2.43.0 (0)\n -->\n<!-- Title: G Pages: 1 -->\n<svg role=\"img\" aria-label=\"some graph title\" width=\"89pt\" height=\"188pt\" viewBox=\"0.00 0.00 89.00 188.00\">\n<title>some graph title</title>\n<desc>\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n</desc>\n\n<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n<title>G</title>\n<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-184 85,-184 85,4 -4,4\" />\n<!-- a -->\n<g id=\"node1\" class=\"node\">\n<title>a</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-162\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">a</text>\n</g>\n<!-- b -->\n<g id=\"node2\" class=\"node\">\n<title>b</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">b</text>\n</g>\n<!-- a->b -->\n<g id=\"edge1\" class=\"edge\">\n<title>a->b</title>\n<path fill=\"none\" stroke=\"black\" d=\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\" />\n</g>\n<!-- c -->\n<g id=\"node3\" class=\"node\">\n<title>c</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-18\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">c</text>\n</g>\n<!-- b->c -->\n<g id=\"edge2\" class=\"edge\">\n<title>b->c</title>\n<path fill=\"none\" stroke=\"black\" d=\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\" />\n</g>\n<!-- c->a -->\n<g id=\"edge3\" class=\"edge\">\n<title>c->a</title>\n<path fill=\"none\" stroke=\"black\" d=\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\" />\n</g>\n</g>\n</svg>\n</div>\n\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\n\n<h4 id=\"q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</h4>\n\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 呢?方法如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}{%{% endraw %} raw %}\n{% raw %}{%{% endraw %} endraw %}\n</code></pre></div></div>\n\n<p>如上,就是用 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 把 <code class=\"language-plaintext highlighter-rouge\">{%</code> 包起来,但是 <code class=\"language-plaintext highlighter-rouge\">%}</code> 不用包。应该讲的很清楚了吧。</p>\n\n<h3 id=\"参考\">参考</h3>\n\n<ol>\n <li><a href=\"https://bundler.io\">https://bundler.io</a></li>\n <li><a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></li>\n <li><a href=\"https://zhuanlan.zhihu.com/p/87225594\">https://zhuanlan.zhihu.com/p/87225594</a></li>\n <li><a href=\"https://chat.openai.com/chat\">https://chat.openai.com/chat</a></li>\n <li><a href=\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\n <li><a href=\"https://github.com/dyutibarma/monochrome\">https://github.com/dyutibarma/monochrome</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\n <li><a href=\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\n <li><a href=\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"ai":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\n \t<meta name=\"description\" content=\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\t\t\n\t<time datetime=\"2022-12-24T15:08:01+00:00\" class=\"by-line\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一自然语言处理领域近年的发展关键节点\" id=\"markdown-toc-一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</a> <ul>\n <li><a href=\"#1从理性主义到经验主义\" id=\"markdown-toc-1从理性主义到经验主义\">1、从理性主义到经验主义</a></li>\n <li><a href=\"#2经验主义的早期还不是深度学习\" id=\"markdown-toc-2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</a></li>\n <li><a href=\"#3撇开特征让机器囫囵吞枣地学吧\" id=\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\n <li><a href=\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\" id=\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\n <li><a href=\"#5自监督学习法让我们省去人工标注\" id=\"markdown-toc-5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</a></li>\n <li><a href=\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\" id=\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\n <li><a href=\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\" id=\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\n <li><a href=\"#8数据和算力有了还不够\" id=\"markdown-toc-8数据和算力有了还不够\">8、数据和算力有了,还不够</a></li>\n </ul>\n </li>\n <li><a href=\"#二学术里程碑几篇重量级论文\" id=\"markdown-toc-二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</a> <ul>\n <li><a href=\"#1提出-transformer-的attention-is-all-you-need2017\" id=\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\n <li><a href=\"#2elmo-deep-contextualized-word-representations\" id=\"markdown-toc-2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</a></li>\n <li><a href=\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\" id=\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\n <li><a href=\"#4gpt-3-language-models-are-few-shot-learners2020\" id=\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\n <li><a href=\"#其他的重量级论文\" id=\"markdown-toc-其他的重量级论文\">其他的重量级论文</a></li>\n </ul>\n </li>\n <li><a href=\"#三行业里程碑\" id=\"markdown-toc-三行业里程碑\">三、行业里程碑</a></li>\n <li><a href=\"#四成本\" id=\"markdown-toc-四成本\">四、成本</a></li>\n <li><a href=\"#五业内应用\" id=\"markdown-toc-五业内应用\">五、业内应用</a></li>\n <li><a href=\"#五行业内哪些人的言论值得我们日常重点关注\" id=\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</h3>\n\n<p><img src=\"/img/src/2022-12-17-ai-bert-1-1.jpg\" alt=\"image\" /></p>\n\n<h4 id=\"1从理性主义到经验主义\">1、从理性主义到经验主义</h4>\n\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\n\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\n\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\n\n<h4 id=\"2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</h4>\n\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\n\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\n\n<h4 id=\"3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\n\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\n\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\n\n<h4 id=\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\n\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\n\n<h4 id=\"5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</h4>\n\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\n\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\n\n<h4 id=\"6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\n\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\n\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\n\n<h4 id=\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\n\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\n\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\n\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\n\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\n\n<h4 id=\"8数据和算力有了还不够\">8、数据和算力有了,还不够</h4>\n\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\n\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\n\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\n\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\n\n<h3 id=\"二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</h3>\n\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\n\n<h4 id=\"1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\n\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\n\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\n\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\n\n<h4 id=\"2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</h4>\n\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\n\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\n\n<h4 id=\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\n\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\n\n<ul>\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\n <li>在预训练中引入自监督学习任务。</li>\n</ul>\n\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\n\n<h4 id=\"4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\n\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\n\n<h4 id=\"其他的重量级论文\">其他的重量级论文</h4>\n\n<ul>\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\n <li>……</li>\n</ul>\n\n<h3 id=\"三行业里程碑\">三、行业里程碑</h3>\n\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\n\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\n\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\n\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\n\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\n\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\n\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\n\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\n\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\n\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\n\n<h3 id=\"四成本\">四、成本</h3>\n\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\n\n<ul>\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\n</ul>\n\n<h3 id=\"五业内应用\">五、业内应用</h3>\n\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\n\n<ul>\n <li>虚拟客服(可以乱真的)</li>\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\n <li>智能翻译</li>\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\n <li>AI 广告公司:替代传统广告公司</li>\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\n</ul>\n\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\n\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\n\n<ul>\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-7.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-2.jpg\" alt=\"image\" /></p>\n\n<ul>\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-8.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年年底,西湖心辰公司发布「<a href=\"https://www.heyfriday.cn/\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-1.png\" alt=\"image\" /></p>\n\n<h3 id=\"五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</h3>\n\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\n\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\n\n<blockquote>\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\n</blockquote>\n\n<blockquote>\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\n</blockquote>\n\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\n\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\n\n<blockquote>\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\n</blockquote>\n\n<blockquote>\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\n</blockquote>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://beta.openai.com/docs/models</li>\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\n <li>https://hub.baai.ac.cn/view/21726</li>\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\n <li>https://www.sohu.com/a/615541698_121255906</li>\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\n \t<meta name=\"description\" content=\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\t\t\n\t<time datetime=\"2022-12-17T15:08:01+00:00\" class=\"by-line\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一关于-bert-的一些背景\" id=\"markdown-toc-一关于-bert-的一些背景\">一、关于 BERT 的一些背景</a></li>\n <li><a href=\"#二开始一个-bert-的动手小试验\" id=\"markdown-toc-二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</a> <ul>\n <li><a href=\"#1安装-anaconda-来为部署-bert-做环境准备\" id=\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\n <li><a href=\"#2安装-bert-所需要的各种依赖\" id=\"markdown-toc-2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</a></li>\n <li><a href=\"#3下载一个预训练pre-train过的-bert-模型\" id=\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\n <li><a href=\"#5启动-bert-服务端\" id=\"markdown-toc-5启动-bert-服务端\">5、启动 BERT 服务端</a></li>\n <li><a href=\"#6在-pycharm-中使用-conda-的环境\" id=\"markdown-toc-6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</a></li>\n <li><a href=\"#7编写程序实现-bert-客户端\" id=\"markdown-toc-7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</a></li>\n </ul>\n </li>\n <li><a href=\"#三bert-模型的优劣势及其原因\" id=\"markdown-toc-三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</a> <ul>\n <li><a href=\"#1bert-的优势是很明显的\" id=\"markdown-toc-1bert-的优势是很明显的\">1、BERT 的优势是很明显的</a> <ul>\n <li><a href=\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\" id=\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\n <li><a href=\"#12识别并专注于较重要的部分进行文本处理\" id=\"markdown-toc-12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\n <li><a href=\"#13快速构建针对具体任务的-nlp-系统\" id=\"markdown-toc-13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\n </ul>\n </li>\n <li><a href=\"#2bert-模型的劣势及其原因\" id=\"markdown-toc-2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</a> <ul>\n <li><a href=\"#21随机挖-mask-的完形填空题是有隐患的\" id=\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\n <li><a href=\"#22nsp-任务有必要吗\" id=\"markdown-toc-22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</a></li>\n <li><a href=\"#23针对两个或以上词组成的连续词的词义被丢失\" id=\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\n <li><a href=\"#24需要的算力高\" id=\"markdown-toc-24需要的算力高\">2.4、需要的算力高</a></li>\n <li><a href=\"#25需要的模型大\" id=\"markdown-toc-25需要的模型大\">2.5、需要的模型大</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四一些关于-bert-的问题\" id=\"markdown-toc-四一些关于-bert-的问题\">四、一些关于 BERT 的问题</a> <ul>\n <li><a href=\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\" id=\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\n <li><a href=\"#2为什么-bert-可以比-rnn-更好地并行化\" id=\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一关于-bert-的一些背景\">一、关于 BERT 的一些背景</h3>\n\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\n\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\n\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\n\n<h3 id=\"二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</h3>\n\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\n\n<h4 id=\"1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\n\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\n\n<p>更新 conda 到最新版本:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda update <span class=\"nt\">-n</span> base conda\n</code></pre></div></div>\n\n<p>使用 Python 3.7 创建一个新的环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda create <span class=\"nt\">-n</span> py37 <span class=\"nv\">python</span><span class=\"o\">=</span>3.7\n</code></pre></div></div>\n\n<p>激活这个新环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda activate py37\n</code></pre></div></div>\n\n<p>验证正在使用的是正确版本的 Python</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>python <span class=\"nt\">--version</span>\n</code></pre></div></div>\n\n<p>另外你可能还会用到的 conda 命令有:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\nconda deactivate py37\n\n<span class=\"c\"># 查看 conda 当前安装的所有库</span>\nconda list\n</code></pre></div></div>\n\n<h4 id=\"2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda <span class=\"nb\">install </span><span class=\"nv\">tensorflow</span><span class=\"o\">==</span>1.14.0\n</code></pre></div></div>\n\n<p>验证 tensorflow 是否安装正确:</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">import</span> <span class=\"nn\">tensorflow</span> <span class=\"k\">as</span> <span class=\"n\">tf</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">tf</span><span class=\"p\">.</span><span class=\"n\">__version__</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<h4 id=\"3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\n\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\n\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\n\n<ul>\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n</ul>\n\n<p>4、安装 BERT 的服务端和客户端</p>\n\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\n\n<p>在你激活的环境里,安装 <code class=\"language-plaintext highlighter-rouge\">bert-as-service</code>:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 安装服务端和客户端</span>\n<span class=\"c\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\nconda <span class=\"nb\">install </span>bert-serving-server bert-serving-client \n验证 bert-as-service 是否安装成功\nbert-serving-start <span class=\"nt\">-h</span>\n</code></pre></div></div>\n\n<h4 id=\"5启动-bert-服务端\">5、启动 BERT 服务端</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 命令行下启动BERT服务</span>\n<span class=\"c\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\nbert-serving-start <span class=\"nt\">-model_dir</span> /模型/的/绝对/路径 <span class=\"nt\">-num_worker</span><span class=\"o\">=</span>4\n</code></pre></div></div>\n\n<h4 id=\"6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</h4>\n\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\n\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\"language-plaintext highlighter-rouge\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\n\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\n\n<h4 id=\"7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</h4>\n\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">from</span> <span class=\"nn\">bert_serving.client</span> <span class=\"kn\">import</span> <span class=\"n\">BertClient</span>\n<span class=\"kn\">import</span> <span class=\"nn\">numpy</span> <span class=\"k\">as</span> <span class=\"n\">np</span>\n\n<span class=\"c1\"># 定义类\n</span><span class=\"k\">class</span> <span class=\"nc\">BertModel</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"nf\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span> <span class=\"o\">=</span> <span class=\"n\">BertClient</span><span class=\"p\">(</span><span class=\"n\">ip</span><span class=\"o\">=</span><span class=\"s\">'127.0.0.1'</span><span class=\"p\">,</span> <span class=\"n\">port</span><span class=\"o\">=</span><span class=\"mi\">5555</span><span class=\"p\">,</span> <span class=\"n\">port_out</span><span class=\"o\">=</span><span class=\"mi\">5556</span><span class=\"p\">)</span> <span class=\"c1\"># 创建客户端对象\n</span> <span class=\"c1\"># 注意:可以参考API,查看其它参数的设置\n</span> <span class=\"c1\"># 127.0.0.1 表示本机IP,也可以用localhost\n</span> <span class=\"k\">except</span><span class=\"p\">:</span>\n <span class=\"k\">raise</span> <span class=\"nb\">Exception</span><span class=\"p\">(</span><span class=\"s\">\"cannot create BertClient\"</span><span class=\"p\">)</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">close_bert</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">sentence_embedding</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">text</span><span class=\"p\">):</span>\n <span class=\"s\">'''对输入文本进行embedding\n Args:\n text: str, 输入文本\n Returns:\n text_vector: float, 返回一个列表,包含text的embedding编码值\n '''</span>\n <span class=\"n\">text_vector</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">encode</span><span class=\"p\">([</span><span class=\"n\">text</span><span class=\"p\">])[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"k\">return</span> <span class=\"n\">text_vector</span> <span class=\"c1\"># 获取输出结果\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">caculate_similarity</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">vec_1</span><span class=\"p\">,</span> <span class=\"n\">vec_2</span><span class=\"p\">):</span>\n <span class=\"s\">'''根据两个语句的vector,计算它们的相似性\n Args:\n vec_1: float, 语句1的vector\n vec_2: float, 语句2的vector\n Returns:\n sim_value: float, 返回相似性的计算值\n '''</span>\n <span class=\"c1\"># 根据cosine的计算公式\n</span> <span class=\"n\">v1</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_1</span><span class=\"p\">)</span>\n <span class=\"n\">v2</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_2</span><span class=\"p\">)</span>\n <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">v1</span> <span class=\"o\">*</span> <span class=\"n\">v2</span><span class=\"p\">.</span><span class=\"n\">T</span><span class=\"p\">)</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v1</span><span class=\"p\">)</span> <span class=\"o\">*</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v2</span><span class=\"p\">)</span>\n <span class=\"n\">cosine</span> <span class=\"o\">=</span> <span class=\"n\">a</span> <span class=\"o\">/</span> <span class=\"n\">b</span>\n <span class=\"k\">return</span> <span class=\"n\">cosine</span>\n\n\n<span class=\"k\">if</span> <span class=\"n\">__name__</span> <span class=\"o\">==</span> <span class=\"s\">\"__main__\"</span><span class=\"p\">:</span>\n <span class=\"c1\"># 创建bert对象\n</span> <span class=\"n\">bert</span> <span class=\"o\">=</span> <span class=\"n\">BertModel</span><span class=\"p\">()</span>\n <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n <span class=\"c1\"># --- 输入语句 ----\n</span> <span class=\"n\">input_a</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句1: '</span><span class=\"p\">)</span>\n\n <span class=\"k\">if</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"N\"</span> <span class=\"ow\">or</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"n\"</span><span class=\"p\">:</span>\n <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">close_bert</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span> <span class=\"k\">break</span>\n\n <span class=\"n\">input_b</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句2: '</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># --- 对输入语句进行embedding ---\n</span>\n <span class=\"n\">a_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_a</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'a_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">a_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"n\">b_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_b</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'b_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 计算两个语句的相似性\n</span> <span class=\"n\">cos</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">caculate_similarity</span><span class=\"p\">(</span><span class=\"n\">a_vec</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'cosine value : '</span><span class=\"p\">,</span> <span class=\"n\">cos</span><span class=\"p\">)</span>\n\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'</span><span class=\"se\">\\n\\n</span><span class=\"s\">'</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\n</span> <span class=\"k\">if</span> <span class=\"n\">cos</span> <span class=\"o\">></span> <span class=\"mf\">0.85</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"2个语句的含义相似\"</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"不相似\"</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<p>在使用 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 连接 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 时,你需要确保 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 使用的模型和 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\n\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>请输入语句1: \n请输入语句2: \n</code></pre></div></div>\n\n<p>两句输入好确认后,得到如下形式的结果:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>a_vec shape : (768,)\nb_vec shape : (768,)\ncosine value : 0.8691698561422959\n</code></pre></div></div>\n\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\n\n<h3 id=\"三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</h3>\n\n<p>论文地址:<a href=\"https://arxiv.org/abs/1810.04805\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\n\n<h4 id=\"1bert-的优势是很明显的\">1、BERT 的优势是很明显的</h4>\n\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\n\n<blockquote>\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\n</blockquote>\n\n<h5 id=\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\n\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\n\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\n\n<h5 id=\"12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</h5>\n\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\n\n<h5 id=\"13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</h5>\n\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\n\n<h4 id=\"2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</h4>\n\n<h5 id=\"21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\n\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\n\n<h5 id=\"22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</h5>\n\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\n\n<h5 id=\"23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\n\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\n\n<h5 id=\"24需要的算力高\">2.4、需要的算力高</h5>\n\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\n\n<h5 id=\"25需要的模型大\">2.5、需要的模型大</h5>\n\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\n\n<h3 id=\"四一些关于-bert-的问题\">四、一些关于 BERT 的问题</h3>\n\n<h4 id=\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\n\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\n\n<h4 id=\"2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\n\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://arxiv.org/abs/1810.04805</li>\n <li>https://github.com/google-research/bert</li>\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\n \t<meta name=\"description\" content=\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\t\t\n\t<time datetime=\"2022-12-11T15:59:57+00:00\" class=\"by-line\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2022-12-11-wechat-chatgpt-3.png\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\n\n<h3 id=\"stepbystep\">Step by step</h3>\n\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\n\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\n\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\n\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\n\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\n\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\n\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">chatGPTAccountPool</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"na\">email</span><span class=\"pi\">:</span> <span class=\"s\"><your email></span>\n <span class=\"na\">password</span><span class=\"pi\">:</span> <span class=\"s\"><your password></span>\n<span class=\"c1\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\n<span class=\"na\">chatPrivateTiggerKeyword</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">\"</span>\n</code></pre></div></div>\n\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker pull holegots/wechat-chatgpt:latest。\n</code></pre></div></div>\n\n<p>第八步,启动 wechat-chatgpt:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker run <span class=\"nt\">-d</span> <span class=\"nt\">--name</span> wechat-chatgpt <span class=\"nt\">-v</span> <span class=\"si\">$(</span><span class=\"nb\">pwd</span><span class=\"si\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\n</code></pre></div></div>\n\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\"language-plaintext highlighter-rouge\">npm install && poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-1.png\" alt=\"image\" style=\"width:100%\" /></th>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-2.png\" alt=\"image\" style=\"width:100%\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\n\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker logs <span class=\"nt\">-f</span> wechat-chatgpt\n</code></pre></div></div>\n\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\n\n<p>如果要停止运行,用如下命令:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker stop wechat-chatgpt\n</code></pre></div></div>\n\n<h3 id=\"参考\">参考</h3>\n\n<p>1、<a href=\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"]},"collections":[{"relative_directory":"_posts","docs":["<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</title>\n \t<meta name=\"description\" content=\"RTMFP 是 Adobe 开发的基于 UDP 协议的实时传输媒体流协议,支持 P2P 传输,具有较高的实时性和安全性。它的主要应用场景是视频通信、语音通信和网络游戏。OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。Cumulus 是一个基于 OpenRTMFP 的服务器,提供 RTMFP 服务。它具有轻量级、跨平台和可扩展的特点,并且还提供了负载均衡和可扩展性解决方案。YY 语音的 Web 端音视频流媒体能力,正是基于 RTMFP 协议做的迭代优化实现的。本文是船长关于这个系列文章的第一篇。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</h2>\t\t\n\t<time datetime=\"2012-04-09T18:57:19+00:00\" class=\"by-line\">09 Apr 2012, 广州 | 作者 麦克船长 | 总计 8401 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一rtmfp是什么\" id=\"markdown-toc-一rtmfp是什么\">一、RTMFP 是什么?</a> <ul>\n <li><a href=\"#文件分享-p2p-和实时流媒体-p2p-的区别是什么\" id=\"markdown-toc-文件分享-p2p-和实时流媒体-p2p-的区别是什么\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</a></li>\n <li><a href=\"#rtmfp-和-rtmp-之间的区别是什么\" id=\"markdown-toc-rtmfp-和-rtmp-之间的区别是什么\">RTMFP 和 RTMP 之间的区别是什么?</a></li>\n <li><a href=\"#flash-player-支持-rtmfp-吗\" id=\"markdown-toc-flash-player-支持-rtmfp-吗\">Flash Player 支持 RTMFP 吗?</a></li>\n <li><a href=\"#cumulus-使用-adobe-的-cirrus-key-吗\" id=\"markdown-toc-cumulus-使用-adobe-的-cirrus-key-吗\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</a></li>\n <li><a href=\"#这个开源项目合法吗\" id=\"markdown-toc-这个开源项目合法吗\">这个开源项目合法吗?</a></li>\n </ul>\n </li>\n <li><a href=\"#二openrtmfp和cumulus\" id=\"markdown-toc-二openrtmfp和cumulus\">二、OpenRTMFP 和 Cumulus</a></li>\n <li><a href=\"#三入门介绍与部署cumulusserver\" id=\"markdown-toc-三入门介绍与部署cumulusserver\">三、入门介绍与部署 CumulusServer</a> <ul>\n <li><a href=\"#1背景介绍\" id=\"markdown-toc-1背景介绍\">1、背景介绍</a></li>\n <li><a href=\"#2准备工作\" id=\"markdown-toc-2准备工作\">2、准备工作</a></li>\n <li><a href=\"#3安装\" id=\"markdown-toc-3安装\">3、安装</a> <ul>\n <li><a href=\"#31外部依赖的安装\" id=\"markdown-toc-31外部依赖的安装\">3.1、外部依赖的安装</a></li>\n <li><a href=\"#32安装openrtmfpcumulus\" id=\"markdown-toc-32安装openrtmfpcumulus\">3.2、安装 OpenRTMFP/Cumulus</a></li>\n </ul>\n </li>\n <li><a href=\"#4配置\" id=\"markdown-toc-4配置\">4、配置</a></li>\n <li><a href=\"#5启动\" id=\"markdown-toc-5启动\">5、启动</a></li>\n <li><a href=\"#6基本使用\" id=\"markdown-toc-6基本使用\">6、基本使用</a></li>\n <li><a href=\"#7扩展cumulusserverserverapplication\" id=\"markdown-toc-7扩展cumulusserverserverapplication\">7、扩展 CumulusServer(Server Application)</a></li>\n </ul>\n </li>\n <li><a href=\"#三用lua编写helloworld应用扩展cumulusserver\" id=\"markdown-toc-三用lua编写helloworld应用扩展cumulusserver\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</a> <ul>\n <li><a href=\"#1server-side\" id=\"markdown-toc-1server-side\">1、Server-side</a> <ul>\n <li><a href=\"#11serverconfiguration\" id=\"markdown-toc-11serverconfiguration\">1.1、Server configuration</a></li>\n <li><a href=\"#12applicationfile\" id=\"markdown-toc-12applicationfile\">1.2、Application file</a></li>\n </ul>\n </li>\n <li><a href=\"#2client-side\" id=\"markdown-toc-2client-side\">2、Client-side</a></li>\n <li><a href=\"#3运行结果\" id=\"markdown-toc-3运行结果\">3、运行结果</a></li>\n <li><a href=\"#4远程测试一个免费的测试服务器\" id=\"markdown-toc-4远程测试一个免费的测试服务器\">4、远程测试:一个免费的测试服务器</a></li>\n </ul>\n </li>\n</ul>\n\n<h3 id=\"一rtmfp是什么\">一、RTMFP 是什么?</h3>\n\n<p>Real-Time Media Flow Protocol(RTMFP)是 Adobe 开发的一种基于 UDP 并支持 P2P 的实时传输媒体流。主要特点是:高传输效率(可以使用压缩和算法来优化流量从而提高传输效率)、高实时性(可以保证媒体流的实时性使得视频通信和其他实时通信更加流畅)、支持 P2P 传输(减少对服务器的依赖从而减少带宽和服务器资源消耗)、高安全性(加密媒体流从而保证其安全性)。</p>\n\n<p>RTMFP 的主要应用场景包括:视频通信(视频聊天和视频会议)、语音通信(语音聊天、电话)、网络游戏。不过 RTMFP 目前仅有 Adobe 开发的版本,所以它并不是个开源项目,而是个商业化服务。那么有没有开源版本呢?</p>\n\n<h4 id=\"文件分享-p2p-和实时流媒体-p2p-的区别是什么\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</h4>\n\n<p>RTMFP 是一个 P2P 系统,但它仅针对实时通信时直接用户到用户之间的通信而设计,不能用于多个对等方之间进行文件共享(使用分段下载)。Facebook 在其 Pipe 应用中使用此协议将大文件直接在两个用户之间传输。</p>\n\n<h4 id=\"rtmfp-和-rtmp-之间的区别是什么\">RTMFP 和 RTMP 之间的区别是什么?</h4>\n\n<p>RTMP 是实时消息协议,RTMFP 代表实时媒体流协议。RTMFP 基于用户数据报协议(UDP),而 RTMP 基于传输控制协议(TCP)。\n与 RTMP 不同,RTMFP 还支持直接从一个 Adobe Flash Player 传输数据到另一个,而无需经过服务器。</p>\n\n<h4 id=\"flash-player-支持-rtmfp-吗\">Flash Player 支持 RTMFP 吗?</h4>\n\n<p>RTMFP 是基于用户数据报协议(UDP)的,而 RTMP 是基于传输控制协议(TCP)的。与 RTMP 不同,RTMFP 还支持直接在两个 Adobe Flash Player 之间发送数据,而不经过服务器。Flash Player 10.0 仅允许一对一通信进行 P2P,但从 10.1 开始允许应用程序级别的多播。Flash Player 查找适当的分发路由(覆盖网络),并可以将其分发到通过 P2P 连接的组。</p>\n\n<h4 id=\"cumulus-使用-adobe-的-cirrus-key-吗\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</h4>\n\n<p>不!当然,这是Cumulus的主要目标:成为Cirrus GPL的替代品。唯一的限制是:你的CPU,内存和单台机器的端口数。</p>\n\n<h4 id=\"这个开源项目合法吗\">这个开源项目合法吗?</h4>\n\n<p>在美国,数字千年版权法(Digital Millennium Copyright Act)规定,逆向工程用于协议互操作性是合法的。你可以在 WikiPedia 上查看相关讨论:</p>\n\n<ul>\n <li>http://en.wikipedia.org/wiki/Real_Time_Media_Flow_Protocol</li>\n <li>http://en.wikipedia.org/wiki/Proprietary_protocol</li>\n <li>http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act</li>\n</ul>\n\n<p>当逆向工程的目的是协议互操作性时,有法律先例。在美国,数字千年版权法(Digital Millennium Copyright Act)为逆向工程软件以使其与其他软件互操作提供了安全保障。</p>\n\n<h3 id=\"二openrtmfp和cumulus\">二、OpenRTMFP 和 Cumulus</h3>\n\n<p>OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。它包含了 RTMFP 协议的实现,以及一些额外的功能,如媒体流传输、P2P 通信、脚本引擎和数据存储。</p>\n\n<p>Cumulus 是一个基于 OpenRTMFP 的服务器,是一个完整的开源且跨平台的 RTMFP 服务器,可通过脚本进行扩展。CumulusServer 根据 GPL 许可在考虑以下 4 个概念的情况下开发:速度、轻量、跨平台和可扩展。尽管尚未发布版本,但只有在 CumulusServer 经过测试和批准后才会将代码推送到 github。实际上,主要稳定的功能有:</p>\n\n<ul>\n <li>P2P rendez-vous service</li>\n <li>live streaming</li>\n <li>RPC、pull、push exchange,实际上客户端和服务器之间的所有 AMF 可能交换</li>\n <li>脚本引擎,用于创建自己的应用服务器或扩展 Cumulus 的功能</li>\n <li>可扩展性和负载均衡解决方案</li>\n</ul>\n\n<p>下面的内容是本篇 blog 的重点,包括两部分:先是 OpenRTMFP 应用的核心 CumulusServer 的入门介绍与部署,然后用 Lua 编写 HelloWorld 应用扩展 CumulusServer,我们开始吧!</p>\n\n<h3 id=\"三入门介绍与部署cumulusserver\">三、入门介绍与部署 CumulusServer</h3>\n\n<h4 id=\"1背景介绍\">1、背景介绍</h4>\n\n<p>OpenRTMFP 可以帮助你实现 Flash 的实时应用的高并发扩展,OpenRTMFP/Cumulus 是基于 GNU General Public License 的。</p>\n\n<p>POCO:POrtable COmponents,是一个强大的开源 C++ 库。其在 C++ 开发中的角色,相当于 Java Class Library、苹果的 Cocoa、.NET framework。</p>\n\n<h4 id=\"2准备工作\">2、准备工作</h4>\n\n<p>下载:</p>\n\n<table>\n <thead>\n <tr>\n <th><strong>External Dependencies</strong></th>\n <th><strong>Official Site</strong></th>\n <th><strong>Windows</strong></th>\n <th><strong>Linux/OSX</strong></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>OpenSSL</td>\n <td><a href=\"http://www.slproweb.com/products/Win32OpenSSL.html\">Official Site</a></td>\n <td><a href=\"http://www.slproweb.com/download/Win32OpenSSL_Light-1_0_1.exe\">Download</a></td>\n <td><a href=\"http://www.openssl.org/source/openssl-1.0.1.tar.gz\">Download</a></td>\n </tr>\n <tr>\n <td>Lua</td>\n <td><a href=\"http://www.lua.org/\">Official Site</a></td>\n <td><a href=\"http://luaforwindows.googlecode.com/files/LuaForWindows_v5.1.4-45.exe\">Download</a></td>\n <td><a href=\"http://www.lua.org/ftp/lua-5.1.5.tar.gz\">Download</a></td>\n </tr>\n <tr>\n <td>POCO</td>\n <td><a href=\"http://pocoproject.org/\">Official Site</a></td>\n <td><a href=\"http://downloads.sourceforge.net/project/poco/sources/poco-1.4.3/poco-1.4.3p1.zip\">Download</a></td>\n <td><a href=\"https://sourceforge.net/projects/poco/files/sources/poco-1.4.3/poco-1.4.3p1.tar.gz/download\">Download</a></td>\n </tr>\n </tbody>\n</table>\n\n<p>注意:POCO for linux 版本必须是 1.4.0 或更高,否则会引起 TCP 相关的 bug。</p>\n\n<h4 id=\"3安装\">3、安装</h4>\n\n<h5 id=\"31外部依赖的安装\">3.1、外部依赖的安装</h5>\n\n<p>Windows 下略,Linux 下基本就是:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>./configuremakesudo\n<span class=\"nv\">$ </span>make <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<h5 id=\"32安装openrtmfpcumulus\">3.2、安装 OpenRTMFP/Cumulus</h5>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">cd </span>OpenRTMFP-Cumulus/CumulusLib\n<span class=\"nv\">$ </span>make\n<span class=\"nv\">$ </span><span class=\"nb\">cd</span> ../CumulusServer\n<span class=\"nv\">$ </span>make\n</code></pre></div></div>\n\n<p>如果出现了 <code class=\"language-plaintext highlighter-rouge\">.h</code> 文件、lib 库找不到的情况,请修改 OpenRTMFP-Cumulus/CumulusLib/Makefile 或 OpenRTMFP-Cumulus/CumulusServer/Makefile。</p>\n\n<h4 id=\"4配置\">4、配置</h4>\n\n<p>通过编写 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP-Cumulus/CumulusServer/CumulusServer.ini</code> 文件来为 OpenRTMFP-Cumulus 进行个性化配置(默认是没有这个文件的),这个文件的内容形如:</p>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">;</span><span class=\"n\">CumulusServer</span><span class=\"p\">.</span><span class=\"n\">ini</span>\n<span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"mi\">1985</span>\n<span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"mi\">114688</span>\n<span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n<span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"mi\">15</span>\n<span class=\"p\">[</span><span class=\"n\">logs</span><span class=\"p\">]</span>\n<span class=\"n\">name</span><span class=\"o\">=</span><span class=\"n\">log</span>\n<span class=\"n\">directory</span><span class=\"o\">=</span><span class=\"n\">C</span><span class=\"p\">:</span><span class=\"o\">/</span><span class=\"n\">CumulusServer</span><span class=\"o\">/</span><span class=\"n\">logs</span>\n</code></pre></div></div>\n\n<p>一些字段的设置含义如下,摘自:<a href=\"https://github.com/OpenRTMFP/Cumulus/wiki/Installation\">地址</a>。</p>\n\n<ul>\n <li>公开给 Client 的端口号 <code class=\"language-plaintext highlighter-rouge\">port</code>,默认值是 1935(RTMFP 服务器的默认端口),用于 CumulusServer 监听 RTMFP 请求。</li>\n <li>UDP 缓冲区字节数 <code class=\"language-plaintext highlighter-rouge\">udpBufferSize</code>, allows to change the size in bytes of UDP reception and sending buffer. Increases this value if your operating system has a default value too lower for important loads.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">keepAliveServer</code>, time in seconds for periodically sending packets keep-alive with server, 15s by default (valid value is from 5s to 255s).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">keepAlivePeer</code>, time in seconds for periodically sending packets keep-alive between peers, 10s by default (valid value is from 5s to 255s).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.activated</code>, activate or not the edges server on the RTMFP server (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, CumulusServer stays a RTMFP server without edges ability (default value is false).</li>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.port</code>, port for the edges server, to accept incoming new CumulusEdge instances (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, it’s the port 1936.</li>\n</ul>\n\n<blockquote>\n <p>Warning: This port will receive plain text request from edges, for this purpose it should not be made public. It’s very important for security consideration. It must be available only for CumulusEdge instances, and anything else.</p>\n</blockquote>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">edges.attemptsBeforeFallback</code>, number of CumulusEdge attempt connections before falling back to CumulusServer (see CumulusEdge, Scalability page for more details about CumulusEdge). By default the value is 2 (in practical, 2 attempts happens after 5 sec approximately).</li>\n <li>SMTP IP 地址 <code class=\"language-plaintext highlighter-rouge\">smtp.host</code>, configure a SMTP host to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is localhost.</li>\n <li>SMTP 端口 <code class=\"language-plaintext highlighter-rouge\">smtp.port</code>, configure a SMTP port to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 25.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">smtp.timeout</code>, configure a SMTP timeout session in seconds to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 60 seconds.</li>\n <li>日志路径 <code class=\"language-plaintext highlighter-rouge\">logs.directory</code>,默认是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer/logsby</code>。</li>\n <li>日志文件名称 <code class=\"language-plaintext highlighter-rouge\">logs.name</code>,默认是<code class=\"language-plaintext highlighter-rouge\">log</code>。</li>\n</ul>\n\n<h4 id=\"5启动\">5、启动</h4>\n\n<p>Windows 下的启动方法为:</p>\n\n<pre><code class=\"language-dos\">$ CumulusServer.exe /registerService [/displayName=CumulusServer /description=\"Open Source RTMFP Server\" /startup=automatic]\n</code></pre>\n\n<p>Unix-like 下的启动方法为:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">sudo</span> ./CumulusServer <span class=\"nt\">--daemon</span> <span class=\"o\">[</span><span class=\"nt\">--pidfile</span><span class=\"o\">=</span>/var/run/CumulusServer.pid]\n</code></pre></div></div>\n\n<p>具体地,我的启动命令为:</p>\n\n<div class=\"language-sh highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">sudo</span> ./CumulusServer <span class=\"nt\">--daemon</span> <span class=\"nt\">--pidfile</span><span class=\"o\">=</span>./CumulusServer.pid\n</code></pre></div></div>\n\n<h4 id=\"6基本使用\">6、基本使用</h4>\n\n<p>本地 Flash client 可以通过如下语句连接:</p>\n\n<div class=\"language-as highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">$</span> <span class=\"kd\">var</span> <span class=\"nx\">nc</span><span class=\"o\">:</span><span class=\"nx\">NetConnection</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nx\">NetConnection</span><span class=\"p\">()</span><span class=\"o\">;</span><span class=\"nx\">nc</span><span class=\"p\">.</span><span class=\"nx\">connect</span><span class=\"p\">(</span><span class=\"s2\">\"rtmfp://localhost/\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>RTMFP默认是采用1935端口,如果你特别指定了其他端口,比如12345,请使用如下方式:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>nc.connect(\"rtmfp://localhost:12345/\");\n</code></pre></div></div>\n\n<h4 id=\"7扩展cumulusserverserverapplication\">7、扩展 CumulusServer(Server Application)</h4>\n\n<p>启动CumulusServer后,会在可执行文件的目录下出现一个www目录,该目录的作用,就是作为 Server Application 的默认根目录。具体的对应关系如下:</p>\n\n<blockquote>\n <p>rtmfp://host:port/ -> [CumulusServer folder]/www/main.lua (root application)</p>\n</blockquote>\n\n<blockquote>\n <p>rtmfp://host:port/myApplication -> [CumulusServer folder]/www/myApplication/main.lua</p>\n</blockquote>\n\n<blockquote>\n <p>rtmfp://host:port/Games/myGame -> [CumulusServer folder]/www/Games/myGame/main.lua</p>\n</blockquote>\n\n<p>另外要提醒的是,如果main.lua文件被修改,则不需要重启 CumulusServer,因为 Server Application 的创建是一种动态的方式。</p>\n\n<h3 id=\"三用lua编写helloworld应用扩展cumulusserver\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</h3>\n\n<p>下面的这个实例是在本地(Client 与 Server 位于同一机器上)测试的。</p>\n\n<h4 id=\"1server-side\">1、Server-side</h4>\n\n<h5 id=\"11serverconfiguration\">1.1、Server configuration</h5>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">;</span> <span class=\"n\">CumulusServer</span><span class=\"p\">.</span><span class=\"n\">ini</span>\n<span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"mi\">1935</span>\n<span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"mi\">114688</span>\n<span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"mi\">10</span>\n<span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"mi\">15</span>\n<span class=\"p\">[</span><span class=\"n\">logs</span><span class=\"p\">]</span><span class=\"n\">name</span> <span class=\"o\">=</span> <span class=\"n\">log</span>\n<span class=\"n\">directory</span> <span class=\"o\">=</span> <span class=\"n\">logs</span>\n</code></pre></div></div>\n\n<h5 id=\"12applicationfile\">1.2、Application file</h5>\n\n<div class=\"language-lua highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">function</span> <span class=\"nf\">onConnection</span><span class=\"p\">(</span><span class=\"n\">client</span><span class=\"p\">,</span><span class=\"n\">response</span><span class=\"p\">,</span><span class=\"o\">...</span><span class=\"p\">)</span>\n <span class=\"k\">function</span> <span class=\"nf\">client</span><span class=\"p\">:</span><span class=\"n\">test</span><span class=\"p\">(</span><span class=\"o\">...</span><span class=\"p\">)</span>\n <span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">firstname</span> <span class=\"o\">=</span> <span class=\"n\">unpack</span><span class=\"p\">(</span><span class=\"n\">arg</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"s2\">\"Hello \"</span><span class=\"o\">..</span><span class=\"n\">firstname</span><span class=\"o\">..</span><span class=\"s2\">\" \"</span><span class=\"o\">..</span><span class=\"n\">name</span>\n <span class=\"k\">end</span>\n <span class=\"k\">end</span>\n</code></pre></div></div>\n\n<h4 id=\"2client-side\">2、Client-side</h4>\n\n<div class=\"language-java highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\">// CumulusClient.as</span>\n\n<span class=\"kn\">package</span> <span class=\"err\">{</span>\n <span class=\"nn\">import</span> <span class=\"n\">flash</span><span class=\"o\">.</span><span class=\"na\">display</span><span class=\"o\">.</span><span class=\"na\">Sprite</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.NetConnection</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.NetStream</span><span class=\"o\">;</span>\n <span class=\"kn\">import</span> <span class=\"nn\">flash.net.Responder</span><span class=\"o\">;</span>\n\n <span class=\"kd\">public</span> <span class=\"kd\">class</span> <span class=\"nc\">CumulusClient</span> <span class=\"kd\">extends</span> <span class=\"nc\">Sprite</span> <span class=\"o\">{</span>\n <span class=\"kd\">private</span> <span class=\"kt\">var</span> <span class=\"nl\">nc:</span><span class=\"nc\">NetConnection</span> <span class=\"o\">=</span> <span class=\"kc\">null</span><span class=\"o\">;</span>\n \t<span class=\"kd\">private</span> <span class=\"kt\">var</span> <span class=\"nl\">ns:</span><span class=\"nc\">NetStream</span> <span class=\"o\">=</span> <span class=\"kc\">null</span><span class=\"o\">;</span>\n \t\n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">CumulusClient</span><span class=\"o\">()</span> <span class=\"o\">{</span>\n <span class=\"n\">nc</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nc\">NetConnection</span><span class=\"o\">();</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">connect</span><span class=\"o\">(</span><span class=\"s\">\"rtmfp://localhost\"</span><span class=\"o\">);</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">client</span> <span class=\"o\">=</span> <span class=\"k\">this</span><span class=\"o\">;</span>\n <span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">call</span><span class=\"o\">(</span><span class=\"s\">\"test\"</span><span class=\"o\">,</span><span class=\"k\">new</span> <span class=\"nc\">Responder</span><span class=\"o\">(</span><span class=\"n\">onResult</span><span class=\"o\">,</span><span class=\"n\">onStatus</span><span class=\"o\">),</span> <span class=\"s\">\"OpenRTMFP/Cumulus\"</span><span class=\"o\">,</span> <span class=\"s\">\"World\"</span><span class=\"o\">)</span>\n <span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">close</span><span class=\"o\">():</span><span class=\"kt\">void</span> <span class=\"o\">{</span> \n\t\t\t<span class=\"n\">nc</span><span class=\"o\">.</span><span class=\"na\">close</span><span class=\"o\">();</span>\n \t<span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">onStatus</span><span class=\"o\">(</span><span class=\"nl\">status:</span><span class=\"nc\">Object</span><span class=\"o\">):</span><span class=\"kt\">void</span> <span class=\"o\">{</span>\n \t<span class=\"n\">trace</span><span class=\"o\">(</span><span class=\"n\">status</span><span class=\"o\">.</span><span class=\"na\">description</span><span class=\"o\">)</span>\n\t <span class=\"o\">}</span>\n \n \t<span class=\"kd\">public</span> <span class=\"n\">function</span> <span class=\"nf\">onResult</span><span class=\"o\">(</span><span class=\"nl\">response:</span><span class=\"nc\">Object</span><span class=\"o\">):</span><span class=\"kt\">void</span> <span class=\"o\">{</span>\n \t<span class=\"n\">trace</span><span class=\"o\">(</span><span class=\"n\">response</span><span class=\"o\">)</span> <span class=\"c1\">// expected to display \"Hello World OpenRTMFP/Cumulus\" </span>\n\t <span class=\"o\">}</span> \n\t<span class=\"o\">}</span>\n<span class=\"o\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3运行结果\">3、运行结果</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Hello World OpenRTMFP/Cumulus\n[SWF] CumulusClient.swf - 解压缩后为 1,776 个字节\n[卸装 SWF] CumulusClient.swf\n</code></pre></div></div>\n\n<h4 id=\"4远程测试一个免费的测试服务器\">4、远程测试:一个免费的测试服务器</h4>\n\n<p>获取 Developer Key 的地址:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>http://108.59.252.39:8080/CumulusServer/index.jsp\n</code></pre></div></div>\n\n<p>服务器配置信息:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Server: amd64 OS: Linux 2.6.18-028stab095.1\nServer IP: 108.59.252.39\nOpenRTMFP as of: 22.Feb.2012\n</code></pre></div></div>\n\n<p>编写服务器段应用地址:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>http://108.59.252.39:8080/CumulusServer/manage_ssls.jsp\n</code></pre></div></div>\n\n<p>快去试试吧 :)</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的第二篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</h2>\t\t\n\t<time datetime=\"2012-04-14T11:20:46+00:00\" class=\"by-line\">14 Apr 2012, 广州 | 作者 麦克船长 | 总计 18890 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一cumulus-启动源码分析\" id=\"markdown-toc-一cumulus-启动源码分析\">一、Cumulus 启动源码分析</a> <ul>\n <li><a href=\"#1maincpp-中的-main-函数\" id=\"markdown-toc-1maincpp-中的-main-函数\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数</a></li>\n <li><a href=\"#2maincpp-中的-cumulusserver-构造函数\" id=\"markdown-toc-2maincpp-中的-cumulusserver-构造函数\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer()</code> 构造函数</a></li>\n <li><a href=\"#3maincpp-中的-cumulusserver-的-initialize-成员函数\" id=\"markdown-toc-3maincpp-中的-cumulusserver-的-initialize-成员函数\">3、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 成员函数</a></li>\n <li><a href=\"#4maincpp-中的-cumulusserver-的-main-成员函数\" id=\"markdown-toc-4maincpp-中的-cumulusserver-的-main-成员函数\">4、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 成员函数</a></li>\n <li><a href=\"#5cumulusserver-是-serverapplication-的子类\" id=\"markdown-toc-5cumulusserver-是-serverapplication-的子类\">5、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的子类</a></li>\n <li><a href=\"#6serverapplication-是-application-的子类\" id=\"markdown-toc-6serverapplication-是-application-的子类\">6、<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的子类</a></li>\n <li><a href=\"#7反初始化\" id=\"markdown-toc-7反初始化\">7、反初始化</a></li>\n </ul>\n </li>\n <li><a href=\"#二cumulusserver-各项交互功能的源码解读\" id=\"markdown-toc-二cumulusserver-各项交互功能的源码解读\">二、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 各项交互功能的源码解读</a> <ul>\n <li><a href=\"#1命令行选项设定\" id=\"markdown-toc-1命令行选项设定\">1、命令行选项设定</a></li>\n <li><a href=\"#2处理命令行选项\" id=\"markdown-toc-2处理命令行选项\">2、处理命令行选项</a></li>\n <li><a href=\"#6dump-logs\" id=\"markdown-toc-6dump-logs\">6、Dump logs</a></li>\n <li><a href=\"#3停止运行\" id=\"markdown-toc-3停止运行\">3、停止运行</a></li>\n <li><a href=\"#4载入配置\" id=\"markdown-toc-4载入配置\">4、载入配置</a></li>\n <li><a href=\"#5处理日志\" id=\"markdown-toc-5处理日志\">5、处理日志</a></li>\n </ul>\n </li>\n <li><a href=\"#三maincpp-的-main-函数源码分析\" id=\"markdown-toc-三maincpp-的-main-函数源码分析\">三、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数源码分析</a> <ul>\n <li><a href=\"#1maincpp-中的-main-函数中的-server\" id=\"markdown-toc-1maincpp-中的-main-函数中的-server\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中的 <code class=\"language-plaintext highlighter-rouge\">server</code></a></li>\n <li><a href=\"#2maincpp-中-main-函数的-serverstart\" id=\"markdown-toc-2maincpp-中-main-函数的-serverstart\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的 <code class=\"language-plaintext highlighter-rouge\">server.start()</code></a></li>\n <li><a href=\"#3回顾一下整个启动流程\" id=\"markdown-toc-3回顾一下整个启动流程\">3、回顾一下整个启动流程</a></li>\n <li><a href=\"#4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\" id=\"markdown-toc-4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>、<code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(...)</code> 函数源码</a></li>\n </ul>\n </li>\n</ul>\n\n<p>本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。首先要知道的是,OpenRTMFP/Cumulus 中使用到的库有 Poco、OpenSSL 和 Lua。</p>\n\n<h3 id=\"一cumulus-启动源码分析\">一、Cumulus 启动源码分析</h3>\n\n<h4 id=\"1maincpp-中的-main-函数\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数</h4>\n\n<p>入口在 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"kt\">int</span> <span class=\"n\">argc</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">argv</span><span class=\"p\">[])</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先检查内存泄露,不过目前这个开发中的项目还没有实现这个功能,只是个空函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">DetectMemoryLeak</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>然后会创建一个匿名的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 对象,并调用其 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,该函数由 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 从 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 中继承而来,而 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 由是从 <code class=\"language-plaintext highlighter-rouge\">Application</code> 继承而来的。<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 对象调用 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,实际是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数,<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数则是调用 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的函数,而该 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数则是先调用 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数,然后调用 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,然后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数。如果 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数被调用时抛出异常,则不会执行 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,但仍然会执行 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Runs the application by performing additional initializations</span>\n <span class=\"c1\">// and calling the main() method.</span>\n <span class=\"k\">return</span> <span class=\"nf\">CumulusServer</span><span class=\"p\">().</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"n\">argc</span><span class=\"p\">,</span> <span class=\"n\">argv</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2maincpp-中的-cumulusserver-构造函数\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer()</code> 构造函数</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的构造函数定义为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">CumulusServer</span><span class=\"p\">()</span><span class=\"o\">:</span> <span class=\"n\">_helpRequested</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span> <span class=\"c1\">// 显示帮助信息</span>\n <span class=\"n\">_pCirrus</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_middle</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">_isInteractive</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_pLogFile</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3maincpp-中的-cumulusserver-的-initialize-成员函数\">3、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 成员函数</h4>\n\n<p>在执行 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数之前,<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 会启动 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数,传入的参数就是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 自己,可以猜到 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> 的 <code class=\"language-plaintext highlighter-rouge\">run</code> 方法中,调用该函数时的参数是 <code class=\"language-plaintext highlighter-rouge\">this</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">initialize</span><span class=\"p\">(</span><span class=\"n\">Application</span><span class=\"o\">&</span> <span class=\"n\">self</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>调用父函数 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的初始化函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">initialize</span><span class=\"p\">(</span><span class=\"n\">self</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>再继续下面的源码分析之前,先要知道,根据 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> ,<code class=\"language-plaintext highlighter-rouge\">Application</code> 类有一些内置的配置属性,如下:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">application.path</code>: 可执行文件的绝对路径;</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.name</code>: 可执行文件的文件名(含扩展名);</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.baseName</code>: 可执行文件的文件名(不含扩展名)</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.dir</code>: 可执行文件的所在目录;</li>\n <li><code class=\"language-plaintext highlighter-rouge\">application.configDir</code>: 配置文件所在目录;</li>\n</ul>\n\n<p>所以下面就读取了可执行文件的所在目录,其中第二个参数表示默认值(即当前目录):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">string</span> <span class=\"n\">dir</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span> <span class=\"s\">\"./\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>然后读取配置文件,目录为上一句所得到的 <code class=\"language-plaintext highlighter-rouge\">dir</code>,文件名(不含扩展名)为 <code class=\"language-plaintext highlighter-rouge\">application.basename</code> 内置配置属性值,其默认值为 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>,然后加上点和扩展名 <code class=\"language-plaintext highlighter-rouge\">.ini</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">loadConfiguration</span><span class=\"p\">(</span><span class=\"n\">dir</span>\n <span class=\"o\">+</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.baseName\"</span><span class=\"p\">,</span> <span class=\"s\">\"CumulusServer\"</span><span class=\"p\">)</span>\n <span class=\"o\">+</span> <span class=\"s\">\".ini\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这样就加载完配置了。然后查看当前的进程是从命令行运行的(命令行是交互的,所以是 interactive),还是以 daemon 方式运行的,这个函数是ServerApplication的一个成员函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_isInteractive</span> <span class=\"o\">=</span> <span class=\"n\">isInteractive</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>然后获取表示日志文件所在目录的字符串,其中 <code class=\"language-plaintext highlighter-rouge\">logs.directory</code> 是外置配置属性(配置文件中),其默认值为上面获取到的可执行文件路径(一般为当前路径)与 <code class=\"language-plaintext highlighter-rouge\">logs</code> 的组合,即一般为当前目录下的 <code class=\"language-plaintext highlighter-rouge\">logs</code> 目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">string</span> <span class=\"nf\">logDir</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"logs.directory\"</span><span class=\"p\">,</span> <span class=\"n\">dir</span> <span class=\"o\">+</span> <span class=\"s\">\"logs\"</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>创建日志文件目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span><span class=\"p\">(</span><span class=\"n\">logDir</span><span class=\"p\">).</span><span class=\"n\">createDirectory</span><span class=\"p\">();</span>\n\n</code></pre></div></div>\n\n<p>日志文件绝对路径,<code class=\"language-plaintext highlighter-rouge\">logs</code> 为默认的日志文件名(不含扩展名的部分),扩展名用0:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logPath</span> <span class=\"o\">=</span> <span class=\"n\">logDir</span> <span class=\"o\">+</span> <span class=\"s\">\"/\"</span> <span class=\"o\">+</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"logs.name\"</span><span class=\"p\">,</span> <span class=\"s\">\"log\"</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"s\">\".\"</span><span class=\"p\">;</span>\n <span class=\"n\">_pLogFile</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nf\">File</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"s\">\"0\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>用日志流打开日志文件(方式为追加写入):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">open</span><span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">path</span><span class=\"p\">(),</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">in</span> <span class=\"o\">|</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">ate</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Logs</code> 是一个方法类(其中的 <code class=\"language-plaintext highlighter-rouge\">public</code> 函数都是静态的),<code class=\"language-plaintext highlighter-rouge\">SetLogger</code> 的作用就是将Logs中的似有静态成员设置为某个 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Logger</code> 对象(由于 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 继承了 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Logger</code>)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetLogger</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4maincpp-中的-cumulusserver-的-main-成员函数\">4、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 成员函数</h4>\n\n<p>OpenRTMFP/Cumulus 是一个基于 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::Application</code> 的服务端应用(准确的说是基于 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::ServerApplication</code> 的服务端应用)。如果没有特殊的启动要求,可以调用 <code class=\"language-plaintext highlighter-rouge\">Poco/Application.h</code> 中定义的宏 <code class=\"language-plaintext highlighter-rouge\">POCO_APP_MAIN</code> 来完成初始化、日志和启动(该宏会根据不同的平台启用不同的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数)。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">run()</code> 函数在调用完 <code class=\"language-plaintext highlighter-rouge\">initialize()</code> 函数后,会调用 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数,该 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的定义在 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">>&</span> <span class=\"n\">args</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>首先看是否是要求帮助信息,<code class=\"language-plaintext highlighter-rouge\">displayHelp</code> 是借助 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::HelpFormatter</code> 实现的,<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的构造函数会在调用时将 <code class=\"language-plaintext highlighter-rouge\">_helpRequested</code> 设置为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_helpRequested</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">displayHelp</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是,则进入启动状态,首先创建一个 <code class=\"language-plaintext highlighter-rouge\">RTMFPServerParams</code> 对象 <code class=\"language-plaintext highlighter-rouge\">params</code>,用来存储 OpenRTMFP/CumulusServer 的基本配置信息。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">RTMFPServerParams</span> <span class=\"n\">params</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">params</code> 存储 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的端口号和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的端口号:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"port\"</span><span class=\"p\">,</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span><span class=\"p\">);</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">edgesPort</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"edges.port\"</span><span class=\"p\">,</span>\n <span class=\"n\">RTMFP_DEFAULT_PORT</span><span class=\"o\">+</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getBool</span><span class=\"p\">(</span><span class=\"s\">\"edges.activated\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">edgesPort</span><span class=\"o\">==</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"edges.port must have a positive value if \\\n edges.activated is true. Server edges is \\\n disactivated.\"</span><span class=\"p\">);</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesPort</span><span class=\"o\">=</span><span class=\"n\">edgesPort</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">_pCirrus</code> 为 <code class=\"language-plaintext highlighter-rouge\">SocketAddress</code> 的成员,是封装IP地址和端口号的对象。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span> <span class=\"o\">=</span> <span class=\"n\">_pCirrus</span><span class=\"p\">;</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">middle</span> <span class=\"o\">=</span> <span class=\"n\">_middle</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>UDB 所使用的缓冲区大小:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"udpBufferSize\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span>\n <span class=\"s\">\"keepAliveServer\"</span><span class=\"p\">,</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"keepAlivePeer\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>失败前 CumulusEdge 的尝试次数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span> <span class=\"o\">=</span> <span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getInt</span><span class=\"p\">(</span>\n <span class=\"s\">\"edges.attemptsBeforeFallback\"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Server</span> <span class=\"nf\">server</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span><span class=\"s\">\"./\"</span><span class=\"p\">),</span>\n <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span><span class=\"n\">config</span><span class=\"p\">());</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">waitForTerminationRequest()</code> 函数是 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中必须调用的,意为等待终止运行的操作请求。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// wait for CTRL-C or kill</span>\n <span class=\"n\">waitForTerminationRequest</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>一旦接收到终止操作的请求,就会执行下面这句,用以退出 OpenRTMFP/Cumulus 的运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Stop the server</span>\n <span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">stop</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">catch</code> 一些可能产生的异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"Configuration problem : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">what</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer unknown error\"</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>OpenRTMFP/CumulusServer 停止运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"n\">Application</span><span class=\"o\">::</span><span class=\"n\">EXIT_OK</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5cumulusserver-是-serverapplication-的子类\">5、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的子类</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 对其子类有如下要求:</p>\n\n<ul>\n <li>Subsystems must be registered in the constructor.</li>\n <li>All non-trivial initializations must be made in the <code class=\"language-plaintext highlighter-rouge\">initialize()</code>` method.</li>\n <li>At the end of the <code class=\"language-plaintext highlighter-rouge\">main()</code> method, <code class=\"language-plaintext highlighter-rouge\">waitForTerminationRequest()</code> should be called.</li>\n</ul>\n\n<h4 id=\"6serverapplication-是-application-的子类\">6、<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的子类</h4>\n\n<p>Application 对其子类的要求是,如下这些成员函数必须被覆盖:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">initialize()</code> (the one-argument, protected variant):上一篇已介绍过。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">uninitialize()</code>:下面会介绍,Application 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数会在调用 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize()</code> 函数。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">reinitialize()</code></li>\n <li><code class=\"language-plaintext highlighter-rouge\">defineOptions()</code>:定义命令行启动选项。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">handleOption()</code>:响应相应的命令行选项。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">main()</code></li>\n</ul>\n\n<h4 id=\"7反初始化\">7、反初始化</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 是继承 <code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 的,<code class=\"language-plaintext highlighter-rouge\">ServerApplication</code> 是继承 <code class=\"language-plaintext highlighter-rouge\">Application</code> 的。<code class=\"language-plaintext highlighter-rouge\">Application</code> 的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数会先调用 <code class=\"language-plaintext highlighter-rouge\">initialize()</code>,然后调用 <code class=\"language-plaintext highlighter-rouge\">main()</code>,最后调用 <code class=\"language-plaintext highlighter-rouge\">uninitialize</code>。最后这个反初始化过程,在 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 就是直接调用父类的 <code class=\"language-plaintext highlighter-rouge\">uninitialize</code> 函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">uninitialize</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">uninitialize</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"二cumulusserver-各项交互功能的源码解读\">二、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 各项交互功能的源码解读</h3>\n\n<h4 id=\"1命令行选项设定\">1、命令行选项设定</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的命令行选项有:<code class=\"language-plaintext highlighter-rouge\">log(l)</code>、<code class=\"language-plaintext highlighter-rouge\">dump(d)</code>、<code class=\"language-plaintext highlighter-rouge\">cirrus(c)</code>、<code class=\"language-plaintext highlighter-rouge\">middle(m)</code>、<code class=\"language-plaintext highlighter-rouge\">help(h)</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">defineOptions</span><span class=\"p\">(</span><span class=\"n\">OptionSet</span><span class=\"o\">&</span> <span class=\"n\">options</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">defineOptions</span><span class=\"p\">(</span><span class=\"n\">options</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>设定日志级别(0 - 8,默认是 6,表示 info 级别)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"log\"</span><span class=\"p\">,</span> <span class=\"s\">\"l\"</span><span class=\"p\">,</span> <span class=\"s\">\"Log level argument, must be beetween 0 and 8 : \\\n nothing, fatal, critic, error, warn, note, info, debug, trace. \\\n Default value is 6 (info), all logs until info level are displayed.\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">required</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">argument</span><span class=\"p\">(</span><span class=\"s\">\"level\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>其他一些选项:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"dump\"</span><span class=\"p\">,</span> <span class=\"s\">\"d\"</span><span class=\"p\">,</span> <span class=\"s\">\"Enables packet traces in logs. Optional arguments \\\n are 'middle' or 'all' respectively to displays just middle packet \\\n process or all packet process. If no argument is given, just outside \\\n packet process will be dumped.\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">,</span><span class=\"s\">\"middle|all\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"cirrus\"</span><span class=\"p\">,</span> <span class=\"s\">\"c\"</span><span class=\"p\">,</span> <span class=\"s\">\"Cirrus address to activate a 'man-in-the-middle' \\\n developer mode in bypassing flash packets to the official cirrus \\\n server of your choice, it's a instable mode to help Cumulus developers, \\\n </span><span class=\"se\">\\\"</span><span class=\"s\">p2p.rtmfp.net:10000</span><span class=\"se\">\\\"</span><span class=\"s\"> for example. By adding the 'dump' argument, \\\n you will able to display Cirrus/Flash packet exchange in your logs \\\n (see 'dump' argument).\"</span><span class=\"p\">,</span><span class=\"nb\">false</span><span class=\"p\">,</span><span class=\"s\">\"address\"</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"middle\"</span><span class=\"p\">,</span> <span class=\"s\">\"m\"</span><span class=\"p\">,</span><span class=\"s\">\"Enables a 'man-in-the-middle' developer mode \\\n between two peers. It's a instable mode to help Cumulus developers. \\\n By adding the 'dump' argument, you will able to display Flash/Flash \\\n packet exchange in your logs (see 'dump' argument).\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n</code></pre></div></div>\n\n<p>显示帮助信息的选项:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">options</span><span class=\"p\">.</span><span class=\"n\">addOption</span><span class=\"p\">(</span>\n <span class=\"n\">Option</span><span class=\"p\">(</span><span class=\"s\">\"help\"</span><span class=\"p\">,</span> <span class=\"s\">\"h\"</span><span class=\"p\">,</span> <span class=\"s\">\"Displays help information about command-line usage.\"</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">required</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">)</span>\n <span class=\"p\">.</span><span class=\"n\">repeatable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">));</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">OptionSet</code> 是 <code class=\"language-plaintext highlighter-rouge\">Poco::Util::OptionSet</code>,调用addOption可以向其中增加选项Option。其中required和repeatable表示:</p>\n\n<p>Sets whether the option is required (flag == true) or optional (flag == false).\nReturns true if the option can be specified more than once, or false if at most once.\n当需要显示帮助信息时,调用如下函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">displayHelp</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">HelpFormatter</span> <span class=\"n\">helpFormatter</span><span class=\"p\">(</span><span class=\"n\">options</span><span class=\"p\">());</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setCommand</span><span class=\"p\">(</span><span class=\"n\">commandName</span><span class=\"p\">());</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setUsage</span><span class=\"p\">(</span><span class=\"s\">\"OPTIONS\"</span><span class=\"p\">);</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">setHeader</span><span class=\"p\">(</span><span class=\"s\">\"CumulusServer, open source RTMFP server\"</span><span class=\"p\">);</span>\n <span class=\"n\">helpFormatter</span><span class=\"p\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">cout</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">setCommand()</code>: Sets the command name.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">setUsage()</code>: Sets the usage string.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">setHeader()</code>: Sets the header string.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">format()</code>: Writes the formatted help text to the given stream.</li>\n</ul>\n\n<h4 id=\"2处理命令行选项\">2、处理命令行选项</h4>\n\n<p>参数是选项名和选项值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">handleOption</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">handleOption</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果选项是帮助:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"help\"</span><span class=\"p\">)</span>\n <span class=\"n\">_helpRequested</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"cirrus\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">URI</span> <span class=\"n\">uri</span><span class=\"p\">(</span><span class=\"s\">\"rtmfp://\"</span><span class=\"o\">+</span><span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">SocketAddress</span><span class=\"p\">(</span><span class=\"n\">uri</span><span class=\"p\">.</span><span class=\"n\">getHost</span><span class=\"p\">(),</span><span class=\"n\">uri</span><span class=\"p\">.</span><span class=\"n\">getPort</span><span class=\"p\">());</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"Mode 'man in the middle' : the exchange will bypass to '%s'\"</span><span class=\"p\">,</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Mode 'man in the middle' error : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">message</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果选项是dump日志:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"dump\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">==</span> <span class=\"s\">\"all\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">ALL</span><span class=\"p\">);</span>\n <span class=\"k\">else</span> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">==</span> <span class=\"s\">\"middle\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">MIDDLE</span><span class=\"p\">);</span>\n <span class=\"k\">else</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetDump</span><span class=\"p\">(</span><span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">EXTERNAL</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果选项是middle:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"middle\"</span><span class=\"p\">)</span>\n <span class=\"n\">_middle</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果选项是log,表示设定日志级别:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">name</span> <span class=\"o\">==</span> <span class=\"s\">\"log\"</span><span class=\"p\">)</span>\n <span class=\"n\">Logs</span><span class=\"o\">::</span><span class=\"n\">SetLevel</span><span class=\"p\">(</span><span class=\"n\">atoi</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">c_str</span><span class=\"p\">()));</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"6dump-logs\">6、Dump logs</h4>\n\n<p>先加一个作用域锁,然后再向日志流写数据。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">dumpHandler</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">data</span><span class=\"p\">,</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_logMutex</span><span class=\"p\">);</span>\n <span class=\"n\">cout</span><span class=\"p\">.</span><span class=\"n\">write</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">data</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">write</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">data</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">manageLogFile</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>调用 manageLogFile,主要做一些日志大小超出限制的处理。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">manageLogFile</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先判断是否超过日志文件的大小上线,LOG_SIZE是1000000字节(即约 1 MB)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">getSize</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"n\">LOG_SIZE</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"kt\">int</span> <span class=\"n\">num</span> <span class=\"o\">=</span> <span class=\"mi\">10</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>打开新日志文件:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span> <span class=\"nf\">file</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"s\">\"10\"</span><span class=\"p\">);</span>\n\n</code></pre></div></div>\n\n<p>如果该文件已经存在,则先删除:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">exists</span><span class=\"p\">())</span>\n <span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">remove</span><span class=\"p\">();</span>\n\n <span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">--</span><span class=\"n\">num</span> <span class=\"o\">>=</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">file</span> <span class=\"o\">=</span> <span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"n\">NumberFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">num</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">exists</span><span class=\"p\">())</span>\n <span class=\"n\">file</span><span class=\"p\">.</span><span class=\"n\">renameTo</span><span class=\"p\">(</span><span class=\"n\">_logPath</span> <span class=\"o\">+</span> <span class=\"n\">NumberFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">num</span> <span class=\"o\">+</span> <span class=\"mi\">1</span><span class=\"p\">));</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">open</span><span class=\"p\">(</span><span class=\"n\">_pLogFile</span><span class=\"o\">-></span><span class=\"n\">path</span><span class=\"p\">(),</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">in</span> <span class=\"o\">|</span> <span class=\"n\">ios</span><span class=\"o\">::</span><span class=\"n\">ate</span><span class=\"p\">);</span>\n <span class=\"err\">}</span> \n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3停止运行\">3、停止运行</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 继承了 <code class=\"language-plaintext highlighter-rouge\">ApplicationKiller</code>,该类中有纯虚函数 <code class=\"language-plaintext highlighter-rouge\">kill()</code> 需要被实现,于是有:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">kill</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">terminate</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ApplicationKiller</code> 的定义在 <code class=\"language-plaintext highlighter-rouge\">ApplicationKiller.h</code> 中,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ApplicationKiller</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"p\">(){}</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">ApplicationKiller</span><span class=\"p\">(){}</span>\n \n <span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">kill</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"4载入配置\">4、载入配置</h4>\n\n<p>在initialize()函数中调用,上一篇已提到过。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">loadConfiguration</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">path</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">ServerApplication</span><span class=\"o\">::</span><span class=\"n\">loadConfiguration</span><span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">);</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5处理日志\">5、处理日志</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">logHandler</span><span class=\"p\">(</span><span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">TID</span> <span class=\"n\">threadId</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">threadName</span><span class=\"p\">,</span>\n <span class=\"n\">Priority</span> <span class=\"n\">priority</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"kt\">char</span> <span class=\"o\">*</span><span class=\"n\">filePath</span><span class=\"p\">,</span>\n <span class=\"kt\">long</span> <span class=\"n\">line</span><span class=\"p\">,</span> \n <span class=\"k\">const</span> <span class=\"kt\">char</span> <span class=\"o\">*</span><span class=\"n\">text</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>作用域锁:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_logMutex</span><span class=\"p\">);</span>\n \n <span class=\"n\">Path</span> <span class=\"nf\">path</span><span class=\"p\">(</span><span class=\"n\">filePath</span><span class=\"p\">);</span>\n <span class=\"n\">string</span> <span class=\"n\">file</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getExtension</span><span class=\"p\">()</span> <span class=\"o\">==</span> <span class=\"s\">\"lua\"</span><span class=\"p\">)</span>\n <span class=\"n\">file</span> <span class=\"o\">+=</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">directory</span><span class=\"p\">(</span><span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">depth</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"s\">\"/\"</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是命令行交互模式(即不是 daemon 模式):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_isInteractive</span><span class=\"p\">)</span>\n <span class=\"n\">printf</span><span class=\"p\">(</span><span class=\"s\">\"%s %s[%ld] %s</span><span class=\"se\">\\n</span><span class=\"s\">\"</span><span class=\"p\">,</span>\n <span class=\"n\">g_logPriorities</span><span class=\"p\">[</span><span class=\"n\">priority</span> <span class=\"o\">-</span> <span class=\"mi\">1</span><span class=\"p\">],</span>\n <span class=\"p\">(</span><span class=\"n\">file</span> <span class=\"o\">+</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getBaseName</span><span class=\"p\">()).</span><span class=\"n\">c_str</span><span class=\"p\">(),</span>\n <span class=\"n\">line</span><span class=\"p\">,</span>\n <span class=\"n\">text</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>向日志流输出一句日志:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_logStream</span> <span class=\"o\"><<</span> <span class=\"n\">DateTimeFormatter</span><span class=\"o\">::</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"n\">LocalDateTime</span><span class=\"p\">(),</span><span class=\"s\">\"%d/%m %H:%M:%S.%c \"</span><span class=\"p\">)</span>\n <span class=\"o\"><<</span> <span class=\"n\">g_logPriorities</span><span class=\"p\">[</span><span class=\"n\">priority</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">]</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'\\t'</span> <span class=\"o\"><<</span> <span class=\"n\">threadName</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'('</span> <span class=\"o\"><<</span> <span class=\"n\">threadId</span> <span class=\"o\"><<</span> <span class=\"s\">\")</span><span class=\"se\">\\t</span><span class=\"s\">\"</span>\n <span class=\"o\"><<</span> <span class=\"p\">(</span><span class=\"n\">file</span> <span class=\"o\">+</span> <span class=\"n\">path</span><span class=\"p\">.</span><span class=\"n\">getFileName</span><span class=\"p\">())</span> \n <span class=\"o\"><<</span> <span class=\"sc\">'['</span> <span class=\"o\"><<</span> <span class=\"n\">line</span> <span class=\"o\"><<</span> <span class=\"s\">\"] \"</span> \n <span class=\"o\"><<</span> <span class=\"n\">text</span> <span class=\"o\"><<</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">endl</span><span class=\"p\">;</span>\n \n <span class=\"n\">_logStream</span><span class=\"p\">.</span><span class=\"n\">flush</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>日志文件的善后处理(主要处理文件大小限制可能产生的问题):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">manageLogFile</span><span class=\"p\">();</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三maincpp-的-main-函数源码分析\">三、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数源码分析</h3>\n\n<h4 id=\"1maincpp-中的-main-函数中的-server\">1、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中的 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数中的 <code class=\"language-plaintext highlighter-rouge\">server</code></h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中真正启动的是 <code class=\"language-plaintext highlighter-rouge\">server</code>,它继承自 <code class=\"language-plaintext highlighter-rouge\">Cumulus::RTMFPServer</code>,而 <code class=\"language-plaintext highlighter-rouge\">Cumulus::RTMFPServer</code> 又继承自 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Startable</code>、<code class=\"language-plaintext highlighter-rouge\">Cumulus::Gateway</code>、<code class=\"language-plaintext highlighter-rouge\">Cumulus::Handler</code>。而 <code class=\"language-plaintext highlighter-rouge\">Cumulus::Startable</code> 继承自 <code class=\"language-plaintext highlighter-rouge\">Poco::Runnable</code>,所以其是一个可以运行的线程。在 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusServer</code> 中,这是主线程。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span> <span class=\"nf\">server</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">().</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"application.dir\"</span><span class=\"p\">,</span> <span class=\"s\">\"./\"</span><span class=\"p\">),</span> <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span> <span class=\"n\">config</span><span class=\"p\">());</span>\n<span class=\"n\">server</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这是 <code class=\"language-plaintext highlighter-rouge\">CumulusServer/Server.h</code> 中定义的,其构造函数的原型为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">root</span><span class=\"p\">,</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"o\">&</span> <span class=\"n\">applicationKiller</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">AbstractConfiguration</span><span class=\"o\">&</span> <span class=\"n\">configurations</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>个参数含义如下:</p>\n\n<blockquote>\n <p>The Path Root for the Server Application Killer for Termanting the Server Application Server Configuration</p>\n</blockquote>\n\n<p>距离来说,在我的 Worksapce 中:</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">root</code> 是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 构造函数的初始化列表极长:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Server</span><span class=\"o\">::</span><span class=\"n\">Server</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">root</span><span class=\"p\">,</span>\n <span class=\"n\">ApplicationKiller</span><span class=\"o\">&</span> <span class=\"n\">applicationKiller</span><span class=\"p\">,</span>\n <span class=\"k\">const</span> <span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">AbstractConfiguration</span><span class=\"o\">&</span> <span class=\"n\">configurations</span><span class=\"p\">)</span> \n <span class=\"o\">:</span> <span class=\"n\">_blacklist</span><span class=\"p\">(</span><span class=\"n\">root</span> <span class=\"o\">+</span> <span class=\"s\">\"blacklist\"</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">),</span>\n <span class=\"n\">_applicationKiller</span><span class=\"p\">(</span><span class=\"n\">applicationKiller</span><span class=\"p\">),</span>\n <span class=\"n\">_hasOnRealTime</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_pService</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">luaMail</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"o\">=</span><span class=\"n\">Script</span><span class=\"o\">::</span><span class=\"n\">CreateState</span><span class=\"p\">(),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getString</span><span class=\"p\">(</span><span class=\"s\">\"smtp.host\"</span><span class=\"p\">,</span><span class=\"s\">\"localhost\"</span><span class=\"p\">),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"smtp.port\"</span><span class=\"p\">,</span><span class=\"n\">SMTPSession</span><span class=\"o\">::</span><span class=\"n\">SMTP_PORT</span><span class=\"p\">),</span>\n <span class=\"n\">configurations</span><span class=\"p\">.</span><span class=\"n\">getInt</span><span class=\"p\">(</span><span class=\"s\">\"smtp.timeout\"</span><span class=\"p\">,</span><span class=\"mi\">60</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>下面调用 <code class=\"language-plaintext highlighter-rouge\">Poco::File</code> 创建目录:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">File</span><span class=\"p\">((</span><span class=\"n\">string</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">WWWPath</span> <span class=\"o\">=</span> <span class=\"n\">root</span> <span class=\"o\">+</span> <span class=\"s\">\"www\"</span><span class=\"p\">).</span><span class=\"n\">createDirectory</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>因为 <code class=\"language-plaintext highlighter-rouge\">roor</code> 是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 目录,所以 <code class=\"language-plaintext highlighter-rouge\">WWWPath</code> 就是 <code class=\"language-plaintext highlighter-rouge\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/www</code> 目录。然后初始化 <code class=\"language-plaintext highlighter-rouge\">GlobalTable</code>,这个 <code class=\"language-plaintext highlighter-rouge\">GlobalTable</code> 是和 Lua 有关的东东,这里暂不细说,先知道与 Lua 相关就好。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Service</span><span class=\"o\">::</span><span class=\"n\">InitGlobalTable</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>下面就涉及到了 Lua script 了:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SCRIPT_BEGIN</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">)</span>\n <span class=\"n\">SCRIPT_CREATE_PERSISTENT_OBJECT</span><span class=\"p\">(</span><span class=\"n\">Invoker</span><span class=\"p\">,</span><span class=\"n\">LUAInvoker</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span>\n <span class=\"n\">readNextConfig</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">,</span><span class=\"n\">configurations</span><span class=\"p\">,</span><span class=\"s\">\"\"</span><span class=\"p\">);</span>\n <span class=\"n\">lua_setglobal</span><span class=\"p\">(</span><span class=\"n\">_pState</span><span class=\"p\">,</span><span class=\"s\">\"cumulus.configs\"</span><span class=\"p\">);</span>\n <span class=\"n\">SCRIPT_END</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">SCRIPT_BEGIN</code>、<code class=\"language-plaintext highlighter-rouge\">SCRIPT_CREATE_PERSISTENT_OBJECT和SCRIPT_END</code> 都是宏,其定义在 <code class=\"language-plaintext highlighter-rouge\">Script.h</code> 文件中,如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#define SCRIPT_BEGIN(STATE) \\\n if (lua_State* __pState = STATE) { \\\n const char* __error=NULL;\n \n#define SCRIPT_CREATE_PERSISTENT_OBJECT(TYPE,LUATYPE,OBJ) \\\n Script::WritePersistentObject<TYPE,LUATYPE>(__pState,OBJ); \\\n lua_pop(__pState,1);\n \n#define SCRIPT_END }\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">SCRIPT_BEGIN和SCRIPT_END</code> 经常用到,当与 Lua 相关的操作出现时,都会以这两个宏作为开头和结尾。</p>\n\n<h4 id=\"2maincpp-中-main-函数的-serverstart\">2、<code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 中 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数的 <code class=\"language-plaintext highlighter-rouge\">server.start()</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">RTMFPServerParams</span><span class=\"o\">&</span> <span class=\"n\">params</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusServer</code> 正在运行,则返回并终止启动。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer server is yet running, call stop method before\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定端口号,如果端口号为 0,则返回并终止启动。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_port</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">port</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_port</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer port must have a positive value\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusEdge</code> 的端口号,如果其端口号与 <code class=\"language-plaintext highlighter-rouge\">OpenRTMFP/CumulusSever</code> 端口号相同,则返回并终止启动:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_edgesPort</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesPort</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_port</span> <span class=\"o\">==</span> <span class=\"n\">_edgesPort</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer port must different than RTMFPServer edges.port\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>Cirrus:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_freqManage</span> <span class=\"o\">=</span> <span class=\"mi\">2000000</span><span class=\"p\">;</span> <span class=\"c1\">// 2 sec by default</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">Target</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">pCirrus</span><span class=\"p\">);</span>\n <span class=\"n\">_freqManage</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span> <span class=\"c1\">// no waiting, direct process in the middle case!</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer started in man-in-the-middle mode with server %s \\\n (unstable debug mode)\"</span><span class=\"p\">,</span> <span class=\"n\">_pCirrus</span><span class=\"o\">-></span><span class=\"n\">address</span><span class=\"p\">.</span><span class=\"n\">toString</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>middle:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_middle</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">middle</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_middle</span><span class=\"p\">)</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer started in man-in-the-middle mode between peers \\\n (unstable debug mode)\"</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>UDP Buffer:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">udpBufferSize</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"o\">==</span><span class=\"mi\">0</span> <span class=\"o\">?</span> \n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">getReceiveBufferSize</span><span class=\"p\">()</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">udpBufferSize</span><span class=\"p\">;</span>\n \n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">setReceiveBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">setSendBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">setReceiveBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">setSendBufferSize</span><span class=\"p\">(</span><span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n \n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Socket buffer receving/sending size = %u/%u\"</span><span class=\"p\">,</span>\n <span class=\"n\">udpBufferSize</span><span class=\"p\">,</span>\n <span class=\"n\">udpBufferSize</span><span class=\"p\">);</span>\n \n <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\"><</span> <span class=\"mi\">5</span> <span class=\"o\">?</span> <span class=\"mi\">5000</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAliveServer</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">=</span> \n <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\"><</span> <span class=\"mi\">5</span> <span class=\"o\">?</span> <span class=\"mi\">5000</span> <span class=\"o\">:</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">keepAlivePeer</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"n\">UInt8</span><span class=\"o\">&</span><span class=\"p\">)</span><span class=\"n\">edgesAttemptsBeforeFallback</span> <span class=\"o\">=</span> <span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">edgesAttemptsBeforeFallback</span><span class=\"p\">;</span>\n \n <span class=\"n\">setPriority</span><span class=\"p\">(</span><span class=\"n\">params</span><span class=\"p\">.</span><span class=\"n\">threadPriority</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>启动线程,进入循环运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">();</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>上句具体的源码实现为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果在运行则返回并终止启动。然后加一个局部锁。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ScopedLock</span><span class=\"o\"><</span><span class=\"n\">FastMutex</span><span class=\"o\">></span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果不得不join()到主线程中,那就join()吧</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>然后就运行这个线程吧:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_terminate</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3回顾一下整个启动流程\">3、回顾一下整个启动流程</h4>\n\n<p>到此我们先回顾一下启动过程:</p>\n\n<p>从 <code class=\"language-plaintext highlighter-rouge\">main.cpp</code> 的启动入口 <code class=\"language-plaintext highlighter-rouge\">main()</code> 函数开始,创建 <code class=\"language-plaintext highlighter-rouge\">Server</code> 对象并启动(调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 函数)。<code class=\"language-plaintext highlighter-rouge\">Server::start()</code> 中调用其父类(<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code>)的父类(<code class=\"language-plaintext highlighter-rouge\">Startable</code>)的方法 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code> 开启线程。\n调用 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code> 函数后,开启线城时传入的参数为 <code class=\"language-plaintext highlighter-rouge\">*this</code>,所以就会运行 <code class=\"language-plaintext highlighter-rouge\">Startable::run()</code>;</p>\n\n<h4 id=\"4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>、<code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(...)</code> 函数源码</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Startable::run()</code> 调用 <code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 函数,但这个函数被 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖,所以会运行 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::prerun()</code>,其源码如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP server starts on %u port\"</span><span class=\"p\">,</span><span class=\"n\">_port</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果CumulusEdge:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP edges server starts on %u port\"</span><span class=\"p\">,</span><span class=\"n\">_edgesPort</span><span class=\"p\">);</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">onStart</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>运行线程:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>处理异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">what</span><span class=\"p\">());</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span> <span class=\"p\">(...)</span> <span class=\"p\">{</span>\n <span class=\"n\">FATAL</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer unknown error\"</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果跳出了,则终止运行:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">onStop</span><span class=\"p\">();</span>\n \n <span class=\"n\">NOTE</span><span class=\"p\">(</span><span class=\"s\">\"RTMFP server stops\"</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>该函数内部又会调用父类的 <code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 函数,该函数调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>它是一个纯虚函数,由 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 实现。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Startable::prerun()</code> 会调用 <code class=\"language-plaintext highlighter-rouge\">void run(const volatile bool& terminate)</code> 方法,该方法被 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">prerun</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">run</span><span class=\"p\">(</span><span class=\"n\">_terminate</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"o\">!</span><span class=\"n\">_terminate</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 覆盖 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的 <code class=\"language-plaintext highlighter-rouge\">run(const volatile bool &terminate)</code> 方法。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">...</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</title>\n \t<meta name=\"description\" content=\"CumulusServer 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 CumulusEdge 和 CumulusServer 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 CumulusServer 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</h2>\t\t\n\t<time datetime=\"2012-04-15T14:26:58+00:00\" class=\"by-line\">15 Apr 2012, 广州 | 作者 麦克船长 | 总计 3844 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#1绑定地址\" id=\"markdown-toc-1绑定地址\">1、绑定地址</a></li>\n <li><a href=\"#2cumulusserver-接收数据\" id=\"markdown-toc-2cumulusserver-接收数据\">2、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 接收数据</a></li>\n <li><a href=\"#3如果-cumulusedge-端口存在且-edge-socket-可用\" id=\"markdown-toc-3如果-cumulusedge-端口存在且-edge-socket-可用\">3、如果 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 端口存在且 edge socket 可用。</a></li>\n <li><a href=\"#4cumulusserver-和-cumulusedge-的-socket-都没有数据\" id=\"markdown-toc-4cumulusserver-和-cumulusedge-的-socket-都没有数据\">4、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 都没有数据:</a></li>\n <li><a href=\"#5发送方的-ip-被禁\" id=\"markdown-toc-5发送方的-ip-被禁\">5、发送方的 ip 被禁:</a></li>\n <li><a href=\"#6数据包长度小于可能的最小值12\" id=\"markdown-toc-6数据包长度小于可能的最小值12\">6、数据包长度小于可能的最小值(12)</a></li>\n</ul>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。</p>\n\n<p>本所要介绍的这个主循环在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run(const volatile bool& terminate)</code> 函数中。RTMFPServer覆盖 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的 <code class=\"language-plaintext highlighter-rouge\">run(const volatile bool &terminate)</code> 方法。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"k\">volatile</span> <span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<h3 id=\"1绑定地址\">1、绑定地址</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 IP 地址和端口:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"nf\">address</span><span class=\"p\">(</span><span class=\"s\">\"0.0.0.0\"</span><span class=\"p\">,</span><span class=\"n\">_port</span><span class=\"p\">);</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">address</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n<span class=\"err\">绑定</span><span class=\"n\">CumulusEdge</span><span class=\"err\">的</span> <span class=\"n\">IP</span> <span class=\"err\">地址和端口:</span>\n\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"nf\">edgesAddress</span><span class=\"p\">(</span><span class=\"s\">\"0.0.0.0\"</span><span class=\"p\">,</span><span class=\"n\">_edgesPort</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">edgesAddress</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>发送者(Client)的 IP 地址和端口:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">SocketAddress</span> <span class=\"n\">sender</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">buff</span><span class=\"p\">[</span><span class=\"n\">PACKETRECV_SIZE</span><span class=\"p\">];</span>\n <span class=\"kt\">int</span> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n \n <span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">terminate</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">stop</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">idle</span> <span class=\"o\">=</span> <span class=\"n\">realTime</span><span class=\"p\">(</span><span class=\"n\">stop</span><span class=\"p\">);</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">stop</span><span class=\"p\">)</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n \n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span><span class=\"o\">=</span><span class=\"nb\">false</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h3 id=\"2cumulusserver-接收数据\">2、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 接收数据</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 有数据可读:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>从 <code class=\"language-plaintext highlighter-rouge\">socket</code> 读取:把数据存到 <code class=\"language-plaintext highlighter-rouge\">buff</code>,把发送者地址赋给 <code class=\"language-plaintext highlighter-rouge\">sender</code>,把所读长度返回给 <code class=\"language-plaintext highlighter-rouge\">size</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">receiveFrom</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span><span class=\"k\">sizeof</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">),</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>处理 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 产生的异常:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Main socket reception : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">address</span><span class=\"p\">,</span><span class=\"nb\">true</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"3如果-cumulusedge-端口存在且-edge-socket-可用\">3、如果 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 端口存在且 edge socket 可用。</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 有数据可读:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"nf\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span> <span class=\"o\">></span> <span class=\"mi\">0</span> <span class=\"o\">&&</span> <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">receiveFrom</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span> <span class=\"k\">sizeof</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">),</span> <span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">catch</span><span class=\"p\">(</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span><span class=\"s\">\"Main socket reception : %s\"</span><span class=\"p\">,</span> <span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">bind</span><span class=\"p\">(</span><span class=\"n\">edgesAddress</span><span class=\"p\">,</span> <span class=\"nb\">true</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">Edge</span><span class=\"o\">*</span> <span class=\"n\">pEdge</span> <span class=\"o\">=</span> <span class=\"n\">edges</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pEdge</span><span class=\"p\">)</span>\n <span class=\"n\">pEdge</span><span class=\"o\">-></span><span class=\"n\">update</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<h3 id=\"4cumulusserver-和-cumulusedge-的-socket-都没有数据\">4、<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 和 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 的 <code class=\"language-plaintext highlighter-rouge\">socket</code> 都没有数据:</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 空闲:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">idle</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>主线程等待一秒。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_timeLastManage</span><span class=\"p\">.</span><span class=\"n\">isElapsed</span><span class=\"p\">(</span><span class=\"n\">_freqManage</span><span class=\"p\">))</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>Just middle session</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_middle</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Sessions</span><span class=\"o\">::</span><span class=\"n\">Iterator</span> <span class=\"n\">it</span><span class=\"p\">;</span>\n <span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span> <span class=\"o\">!=</span> <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Middle</span><span class=\"o\">*</span> <span class=\"n\">pMiddle</span> <span class=\"o\">=</span> <span class=\"k\">dynamic_cast</span><span class=\"o\"><</span><span class=\"n\">Middle</span><span class=\"o\">*></span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMiddle</span><span class=\"p\">)</span>\n <span class=\"n\">pMiddle</span><span class=\"o\">-></span><span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_timeLastManage</span><span class=\"p\">.</span><span class=\"n\">update</span><span class=\"p\">();</span>\n <span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"err\">}</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"5发送方的-ip-被禁\">5、发送方的 ip 被禁:</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isBanned</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">.</span><span class=\"n\">host</span><span class=\"p\">()))</span> <span class=\"p\">{</span>\n <span class=\"n\">INFO</span><span class=\"p\">(</span><span class=\"s\">\"Data rejected because client %s is banned\"</span><span class=\"p\">,</span>\n <span class=\"n\">sender</span><span class=\"p\">.</span><span class=\"n\">host</span><span class=\"p\">().</span><span class=\"n\">toString</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"6数据包长度小于可能的最小值12\">6、数据包长度小于可能的最小值(12)</h3>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">size</span> <span class=\"o\"><</span> <span class=\"n\">RTMFP_MIN_PACKET_SIZE</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Invalid packet\"</span><span class=\"p\">);</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">PacketReader</span> <span class=\"nf\">packet</span><span class=\"p\">(</span><span class=\"n\">buff</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Session</span><span class=\"o\">*</span> <span class=\"n\">pSession</span> <span class=\"o\">=</span> <span class=\"n\">findSession</span><span class=\"p\">(</span><span class=\"n\">RTMFP</span><span class=\"o\">::</span><span class=\"n\">Unpack</span><span class=\"p\">(</span><span class=\"n\">packet</span><span class=\"p\">));</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">pSession</span><span class=\"p\">)</span>\n <span class=\"k\">continue</span><span class=\"p\">;</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">checked</span><span class=\"p\">)</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">commitCookie</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pSession</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>给 <code class=\"language-plaintext highlighter-rouge\">CumulusEdge</code> 或者自己(<code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>)的 <code class=\"language-plaintext highlighter-rouge\">socket</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">setEndPoint</span><span class=\"p\">(</span><span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">isEdges</span> <span class=\"o\">?</span> <span class=\"n\">_edgesSocket</span> <span class=\"o\">:</span> <span class=\"n\">_socket</span><span class=\"p\">,</span><span class=\"n\">sender</span><span class=\"p\">);</span>\n <span class=\"n\">pSession</span><span class=\"o\">-></span><span class=\"n\">receive</span><span class=\"p\">(</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"n\">_socket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_edgesPort</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">();</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_pCirrus</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">delete</span> <span class=\"n\">_pCirrus</span><span class=\"p\">;</span>\n <span class=\"n\">_pCirrus</span> <span class=\"o\">=</span> <span class=\"nb\">NULL</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</h2>\t\t\n\t<time datetime=\"2012-04-24T02:04:55+00:00\" class=\"by-line\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 30820 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一amf-数据类型定义\" id=\"markdown-toc-一amf-数据类型定义\">一、AMF 数据类型定义</a> <ul>\n <li><a href=\"#1数据类型\" id=\"markdown-toc-1数据类型\">1、数据类型</a></li>\n <li><a href=\"#2undefined-type\" id=\"markdown-toc-2undefined-type\">2、<code class=\"language-plaintext highlighter-rouge\">undefined</code> Type</a></li>\n <li><a href=\"#3null-type\" id=\"markdown-toc-3null-type\">3、<code class=\"language-plaintext highlighter-rouge\">null</code> Type</a></li>\n <li><a href=\"#4false-type\" id=\"markdown-toc-4false-type\">4、<code class=\"language-plaintext highlighter-rouge\">false</code> type</a></li>\n <li><a href=\"#5true-type\" id=\"markdown-toc-5true-type\">5、<code class=\"language-plaintext highlighter-rouge\">true</code> type</a></li>\n <li><a href=\"#6integer-type\" id=\"markdown-toc-6integer-type\">6、<code class=\"language-plaintext highlighter-rouge\">integer</code> type</a></li>\n <li><a href=\"#7double-type\" id=\"markdown-toc-7double-type\">7、<code class=\"language-plaintext highlighter-rouge\">double</code> type</a></li>\n <li><a href=\"#8string-type\" id=\"markdown-toc-8string-type\">8、<code class=\"language-plaintext highlighter-rouge\">String</code> type</a></li>\n <li><a href=\"#9xmldocument-type\" id=\"markdown-toc-9xmldocument-type\">9、<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> type</a></li>\n <li><a href=\"#10date-type\" id=\"markdown-toc-10date-type\">10、<code class=\"language-plaintext highlighter-rouge\">Date</code> type</a></li>\n <li><a href=\"#11array-type\" id=\"markdown-toc-11array-type\">11、<code class=\"language-plaintext highlighter-rouge\">Array</code> type</a></li>\n <li><a href=\"#12object-type\" id=\"markdown-toc-12object-type\">12、<code class=\"language-plaintext highlighter-rouge\">Object</code> type</a></li>\n <li><a href=\"#13xml-type\" id=\"markdown-toc-13xml-type\">13、<code class=\"language-plaintext highlighter-rouge\">XML</code> type</a></li>\n <li><a href=\"#14bytearray-type\" id=\"markdown-toc-14bytearray-type\">14、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> type</a></li>\n <li><a href=\"#15amf3-的使用\" id=\"markdown-toc-15amf3-的使用\">15、AMF3 的使用</a> <ul>\n <li><a href=\"#151netconnection-and-amf-3\" id=\"markdown-toc-151netconnection-and-amf-3\">15.1、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> and AMF 3</a></li>\n <li><a href=\"#152netconnection-in-actionscript-30\" id=\"markdown-toc-152netconnection-in-actionscript-30\">15.2、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> in ActionScript 3.0</a></li>\n <li><a href=\"#153bytearray-idatainput-and-idataoutput\" id=\"markdown-toc-153bytearray-idatainput-and-idataoutput\">15.3、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code>, <code class=\"language-plaintext highlighter-rouge\">IDataInput</code> and <code class=\"language-plaintext highlighter-rouge\">IDataOutput</code></a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#二binaryreaderwriter\" id=\"markdown-toc-二binaryreaderwriter\">二、<code class=\"language-plaintext highlighter-rouge\">BinaryReader/Writer</code></a> <ul>\n <li><a href=\"#1amf3-数据格式基础\" id=\"markdown-toc-1amf3-数据格式基础\">1、AMF3 数据格式基础</a></li>\n <li><a href=\"#2序列化\" id=\"markdown-toc-2序列化\">2、序列化</a></li>\n <li><a href=\"#3反序列化\" id=\"markdown-toc-3反序列化\">3、反序列化</a></li>\n </ul>\n </li>\n <li><a href=\"#三packetreaderwriter\" id=\"markdown-toc-三packetreaderwriter\">三、<code class=\"language-plaintext highlighter-rouge\">PacketReader/Writer</code></a> <ul>\n <li><a href=\"#1packetreader\" id=\"markdown-toc-1packetreader\">1、PacketReader</a> <ul>\n <li><a href=\"#11封装-memoryinputstream\" id=\"markdown-toc-11封装-memoryinputstream\">1.1、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code></a></li>\n <li><a href=\"#12收缩缓冲区\" id=\"markdown-toc-12收缩缓冲区\">1.2、收缩缓冲区</a></li>\n <li><a href=\"#13构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-13构造函数拷贝构造函数和析构函数\">1.3、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n <li><a href=\"#2packetwriter\" id=\"markdown-toc-2packetwriter\">2、<code class=\"language-plaintext highlighter-rouge\">PacketWriter</code></a> <ul>\n <li><a href=\"#21封装memoryoutputstream\" id=\"markdown-toc-21封装memoryoutputstream\">2.1、封装<code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code></a></li>\n <li><a href=\"#22封装-binarywriter\" id=\"markdown-toc-22封装-binarywriter\">2.2、封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code></a></li>\n <li><a href=\"#23构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-23构造函数拷贝构造函数和析构函数\">2.3、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四amfreader\" id=\"markdown-toc-四amfreader\">四、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code></a> <ul>\n <li><a href=\"#1objectdef\" id=\"markdown-toc-1objectdef\">1、<code class=\"language-plaintext highlighter-rouge\">ObjectDef</code></a></li>\n <li><a href=\"#2amfreader-定义\" id=\"markdown-toc-2amfreader-定义\">2、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 定义</a> <ul>\n <li><a href=\"#21构造函数析构函数\" id=\"markdown-toc-21构造函数析构函数\">2.1、构造函数、析构函数</a></li>\n <li><a href=\"#22简单封装-packetreader-的一些函数\" id=\"markdown-toc-22简单封装-packetreader-的一些函数\">2.2、简单封装 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 的一些函数</a></li>\n <li><a href=\"#23设置-gptr-位置\" id=\"markdown-toc-23设置-gptr-位置\">2.3、设置 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 位置</a></li>\n <li><a href=\"#24判断类型\" id=\"markdown-toc-24判断类型\">2.4、判断类型</a></li>\n </ul>\n </li>\n <li><a href=\"#3解析-as3-null\" id=\"markdown-toc-3解析-as3-null\">3、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Null</code></a></li>\n <li><a href=\"#4解析-as3-number\" id=\"markdown-toc-4解析-as3-number\">4、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Number</code></a></li>\n <li><a href=\"#5解析-as3-integer\" id=\"markdown-toc-5解析-as3-integer\">5、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Integer</code></a></li>\n <li><a href=\"#6解析-as3-boolean\" id=\"markdown-toc-6解析-as3-boolean\">6、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Boolean</code></a></li>\n <li><a href=\"#7开始引用与结束引用\" id=\"markdown-toc-7开始引用与结束引用\">7、开始引用与结束引用</a></li>\n <li><a href=\"#8解析-as3-bytearray\" id=\"markdown-toc-8解析-as3-bytearray\">8、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code></a></li>\n <li><a href=\"#9解析-as3-date\" id=\"markdown-toc-9解析-as3-date\">9、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Date</code></a></li>\n <li><a href=\"#10解析-as3-dictionary\" id=\"markdown-toc-10解析-as3-dictionary\">10、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Dictionary</code></a></li>\n </ul>\n </li>\n</ul>\n\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。</p>\n\n<h3 id=\"一amf-数据类型定义\">一、AMF 数据类型定义</h3>\n\n<h4 id=\"1数据类型\">1、数据类型</h4>\n\n<p>各种数据类型的标示都在 AMF.h 中定义为宏</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cp\">#define AMF_NUMBER 0x00 // 浮点数\n#define AMF_BOOLEAN 0x01 // 布尔型\n#define AMF_STRING 0x02 // 字符串\n#define AMF_BEGIN_OBJECT 0x03 // 对象,开始\n#define AMF_NULL 0x05 // null\n#define AMF_UNDEFINED 0x06\n#define AMF_REFERENCE 0x07\n#define AMF_MIXED_ARRAY 0x08\n#define AMF_END_OBJECT 0x09 // 对象,结束\n#define AMF_BEGIN_TYPED_OBJECT 0x10\n#define AMF_STRICT_ARRAY 0x0A\n#define AMF_DATE 0x0B // 日期\n#define AMF_LONG_STRING 0x0C // 字符串\n#define AMF_UNSUPPORTED 0x0D\n</span> \n<span class=\"cp\">#define AMF_AVMPLUS_OBJECT 0x11\n#define AMF_END 0xFF\n</span> \n<span class=\"cp\">#define AMF3_UNDEFINED 0x00\n#define AMF3_NULL 0x01\n#define AMF3_FALSE 0x02\n#define AMF3_TRUE 0x03\n#define AMF3_INTEGER 0x04\n#define AMF3_NUMBER 0x05\n#define AMF3_STRING 0x06\n#define AMF3_DATE 0x08\n#define AMF3_ARRAY 0x09\n#define AMF3_OBJECT 0x0A\n#define AMF3_BYTEARRAY 0x0C\n#define AMF3_DICTIONARY 0x11\n</span></code></pre></div></div>\n\n<p>并定义了一个枚举类表示数据类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">AMF</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"k\">enum</span> <span class=\"n\">Type</span> <span class=\"p\">{</span>\n <span class=\"n\">Null</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">,</span>\n <span class=\"n\">Boolean</span><span class=\"p\">,</span>\n <span class=\"n\">Integer</span><span class=\"p\">,</span>\n <span class=\"n\">Number</span><span class=\"p\">,</span>\n <span class=\"n\">String</span><span class=\"p\">,</span>\n <span class=\"n\">Date</span><span class=\"p\">,</span>\n <span class=\"n\">Array</span><span class=\"p\">,</span>\n <span class=\"n\">Object</span><span class=\"p\">,</span>\n <span class=\"n\">ByteArray</span><span class=\"p\">,</span>\n <span class=\"n\">Dictionary</span><span class=\"p\">,</span>\n <span class=\"n\">RawObjectContent</span><span class=\"p\">,</span>\n <span class=\"n\">End</span>\n <span class=\"p\">};</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"2undefined-type\">2、<code class=\"language-plaintext highlighter-rouge\">undefined</code> Type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">undefined</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">undefined</code> 类型标记表示。此值不会编码任何其他信息。</p>\n\n<h4 id=\"3null-type\">3、<code class=\"language-plaintext highlighter-rouge\">null</code> Type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">null</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">null</code> 类型标记表示。此值不会编码任何其他信息。</p>\n\n<h4 id=\"4false-type\">4、<code class=\"language-plaintext highlighter-rouge\">false</code> type</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">false</code> 类型由 <code class=\"language-plaintext highlighter-rouge\">false</code> 类型标记表示,用于编码布尔值 <code class=\"language-plaintext highlighter-rouge\">false</code>。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\n\n<h4 id=\"5true-type\">5、<code class=\"language-plaintext highlighter-rouge\">true</code> type</h4>\n\n<p>true 类型由 true 类型标记表示,用于编码布尔值 true。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\n\n<h4 id=\"6integer-type\">6、<code class=\"language-plaintext highlighter-rouge\">integer</code> type</h4>\n\n<p>在 AMF 3 中,整数使用可变长度的无符号 29 位整数进行序列化。ActionScript 3.0 中的整数类型 - 有符号 <code class=\"language-plaintext highlighter-rouge\">int</code> 类型和无符号 <code class=\"language-plaintext highlighter-rouge\">uint</code> 类型 - 也使用 29 位在 AVM+中表示。如果无符号整数 (<code class=\"language-plaintext highlighter-rouge\">uint</code>) 的值大于等于 229 或者如果有符号整数 (<code class=\"language-plaintext highlighter-rouge\">int</code>) 的值大于等于 228,则它将被 AVM+ 表示为 <code class=\"language-plaintext highlighter-rouge\">double</code> 类型,并使用 AMF 3 double 类型进行序列化。</p>\n\n<h4 id=\"7double-type\">7、<code class=\"language-plaintext highlighter-rouge\">double</code> type</h4>\n\n<p>AMF 3 的 <code class=\"language-plaintext highlighter-rouge\">double</code> 类型与 AMF 0 的 <code class=\"language-plaintext highlighter-rouge\">Number</code> 类型编码方式相同。此类型用于编码 ActionScript <code class=\"language-plaintext highlighter-rouge\">Number</code> 或值大于等于 228 的 ActionScript <code class=\"language-plaintext highlighter-rouge\">int</code> 或值大于等于 229 的 ActionScript <code class=\"language-plaintext highlighter-rouge\">uint</code>。编码值始终是网络字节顺序中的 8 字节 IEEE-754 双精度浮点值 (低内存中的符号位)。</p>\n\n<h4 id=\"8string-type\">8、<code class=\"language-plaintext highlighter-rouge\">String</code> type</h4>\n\n<p>ActionScript String 值使用 AMF 3 中的单个 string 类型表示 - AMF 0 中的 <code class=\"language-plaintext highlighter-rouge\">string</code> 和 <code class=\"language-plaintext highlighter-rouge\">long string</code> 类型的概念不再使用。可以使用对隐式字符串引用表中的索引将字符串作为先前发生的字符串的引用发送。字符串使用 UTF-8 编码 - 但是头可以描述字符串文本或字符串引用。空字符串永远不会作为引用发送。</p>\n\n<h4 id=\"9xmldocument-type\">9、<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> type</h4>\n\n<p>ActionScript 3.0 引入了新的 XML 类型 (参见 3.13),但是旧版的 XMLDocument 类型在语言中被保留为 <code class=\"language-plaintext highlighter-rouge\">flash.xml.XMLDocument</code>。与 AMF 0 类似,<code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> 的结构需要扁平化为字符串表示以进行序列化。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。XMLDocuments 可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\"language-plaintext highlighter-rouge\">XMLDocument</code> 实例的引用发送。</p>\n\n<h4 id=\"10date-type\">10、<code class=\"language-plaintext highlighter-rouge\">Date</code> type</h4>\n\n<p>在 AMF 3 中,ActionScript Date 简单地作为自 1970 年 1 月 1 日午夜 (UTC 时区) 以来的毫秒数进行序列化。不发送本地时区信息。可以使用对隐式对象引用表中的索引将日期作为先前发生的日期实例的引用发送。</p>\n\n<h4 id=\"11array-type\">11、<code class=\"language-plaintext highlighter-rouge\">Array</code> type</h4>\n\n<p>ActionScript 数组的类型和在数组中的位置是基于它们的索引性质描述的。以下表格概述了这些术语的含义:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">strict</code>:仅包含序数(数字)索引</li>\n <li><code class=\"language-plaintext highlighter-rouge\">dense</code>:序数索引从 0 开始,并且在连续索引之间不存在间隙(即,从 0 到数组长度的每一个索引都被定义了)</li>\n <li><code class=\"language-plaintext highlighter-rouge\">sparse</code>:包含至少两个索引之间的一个间隙</li>\n <li><code class=\"language-plaintext highlighter-rouge\">associative</code>:包含至少一个非序数(字符串)索引(有时称为 ECMA 数组)</li>\n</ul>\n\n<p>AMF 将数组分为两部分,密集部分和关联部分。关联部分的二进制表示由名称/值对(可能没有)终止的空字符串。密集部分的二进制表示由密集部分的大小(可能为零)以及有序的值列表(可能没有)组成。在 AMF 中写入的顺序是密集部分的大小,一个以空字符串终止的名称/值对列表,然后是大小的值。数组可以通过使用隐式对象引用表的索引作为先前发生的数组的引用来发送。</p>\n\n<h4 id=\"12object-type\">12、<code class=\"language-plaintext highlighter-rouge\">Object</code> type</h4>\n\n<p>AMF 3 中有一种类型用于处理 ActionScript 对象和自定义用户类。使用术语 “traits” 来描述类的定义特征。除了 “anonymous” 对象和 “typed” 对象,ActionScript 3.0 还引入了两个进一步的 traits 来描述如何序列化对象,即 “dynamic” 和 “externalizable”。以下表格概述了这些术语和它们的含义:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">Anonymous</code>:实际的 ActionScript 对象类型的实例或没有注册别名的类的实例(在反序列化时将其视为对象)。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Typed</code>:具有注册别名的类的实例。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Dynamic</code>:具有动态特征声明的类定义的实例;可以在运行时动态地从实例中添加和删除公共变量成员。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">Externalizable</code>:实现 flash.utils.IExternalizable 的类的实例,它完全控制其成员的序列化(特征信息中不包含属性名)。</li>\n</ul>\n\n<p>在这些特征之外,对象的特征信息还可能包括在类上定义的一组公共变量和公共可读写属性名称(即不是函数的公共成员)。成员名称的顺序很重要,因为在特征信息之后的成员值将按照完全相同的顺序出现。这些成员被视为密封成员,因为它们是由类型明确定义的。</p>\n\n<p>如果类型是动态的,则在密封成员之后可以包括一个进一步的部分,该部分将动态成员列为名称/值对。当遇到空字符串名称时,继续读取动态成员。</p>\n\n<p>对象可以通过使用隐式对象引用表中的索引来作为先前发生对象的引用。此外,还可以通过使用隐式特征引用表中的索引将特征信息发送为先前发生的一组特征的引用。</p>\n\n<h4 id=\"13xml-type\">13、<code class=\"language-plaintext highlighter-rouge\">XML</code> type</h4>\n\n<p>ActionScript 3.0 引入了一种新的 <code class=\"language-plaintext highlighter-rouge\">XML</code> 类型,支持 E4X 语法。为了序列化,需要将 <code class=\"language-plaintext highlighter-rouge\">XML</code> 类型展平成字符串表示形式。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。<code class=\"language-plaintext highlighter-rouge\">XML</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 XML 实例的引用发送。请注意,这种编码对 <code class=\"language-plaintext highlighter-rouge\">XML</code> 的使用造成了一些理论限制。每个 UTF-8 编码的 <code class=\"language-plaintext highlighter-rouge\">XML</code> 实例的字节长度最大为 228-1 字节(大约 256 MB)。</p>\n\n<h4 id=\"14bytearray-type\">14、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> type</h4>\n\n<p>用于保存字节数组,即 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>。AMF 3 使用可变长度编码 29 位整数序列化此类型,其中包括字节长度前缀,然后是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 的原始字节。<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实例的引用发送。</p>\n\n<h4 id=\"15amf3-的使用\">15、AMF3 的使用</h4>\n\n<h5 id=\"151netconnection-and-amf-3\">15.1、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> and AMF 3</h5>\n\n<p>除了序列化 ActionScript 类型外,AMF 还可用于远程服务的异步调用。可使用简单的消息结构将一批请求发送到远程端点。此消息结构的格式为 AMF 0(参见[AMF0])。可以使用特殊的 <code class=\"language-plaintext highlighter-rouge\">avmplus-object-marker</code> 类型将上下文头值或消息正文切换到 AMF 3 编码。</p>\n\n<h5 id=\"152netconnection-in-actionscript-30\">15.2、<code class=\"language-plaintext highlighter-rouge\">NetConnection</code> in ActionScript 3.0</h5>\n\n<p>在 ActionScript 3.0 中,NetConnection 的限定类名是 flash.net.NetConnection。这个类仍然使用响应器来处理远程端点的结果和状态响应,但是现在需要强类型的 Responder 类。完全限定的类名是 flash.net.Responder。除了正常的结果和状态响应之外,NetConnection 还会分发事件,开发人员可以添加监听器。下面是这些事件的概述:</p>\n\n<ul>\n <li>当异常异步抛出时触发,例如来自本机异步代码。</li>\n <li>当输入或输出错误导致网络操作失败时触发。</li>\n <li>当 NetConnection 对象报告其状态或错误条件时触发。</li>\n <li>如果对 NetConnection.call() 的调用尝试连接到调用者安全沙箱外的服务器,则会触发。</li>\n</ul>\n\n<h5 id=\"153bytearray-idatainput-and-idataoutput\">15.3、<code class=\"language-plaintext highlighter-rouge\">ByteArray</code>, <code class=\"language-plaintext highlighter-rouge\">IDataInput</code> and <code class=\"language-plaintext highlighter-rouge\">IDataOutput</code></h5>\n\n<p>ActionScript 3.0 引入了一种新类型,用于支持以字节数组形式处理原始数据,即 <code class=\"language-plaintext highlighter-rouge\">flash.utils.ByteArray</code>。为了协助 ActionScript 对象序列化和复制,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 实现了 <code class=\"language-plaintext highlighter-rouge\">flash.utils.IDataInput</code> 和 <code class=\"language-plaintext highlighter-rouge\">flash.utils.IDataOutput</code>。这些接口指定了帮助将常见类型写入字节流的实用方法。两个感兴趣的方法是 <code class=\"language-plaintext highlighter-rouge\">IDataOutput.writeObject</code> 和 <code class=\"language-plaintext highlighter-rouge\">IDataInput.readObject</code>。这些方法使用 AMF 编码对象。使用的 AMF 版本由 <code class=\"language-plaintext highlighter-rouge\">ByteArray.objectEncoding</code> 方法控制,该方法可以设置为 AMF 3 或 AMF 0。枚举类型 <code class=\"language-plaintext highlighter-rouge\">flash.net.ObjectEncoding</code> 包含 AMF 版本的常量:分别为 <code class=\"language-plaintext highlighter-rouge\">ObjectEncoding.AMF0</code> 和 <code class=\"language-plaintext highlighter-rouge\">ObjectEncoding.AMF3</code>。</p>\n\n<p>请注意,<code class=\"language-plaintext highlighter-rouge\">ByteArray.writeObject</code> 使用一个版本的 AMF 对整个对象进行编码。与 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 不同,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 不会从 AMF 0 开始,然后将 <code class=\"language-plaintext highlighter-rouge\">objectEncoding</code> 属性设置为 AMF 3 并切换到 AMF 3。还请注意,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 为每个 <code class=\"language-plaintext highlighter-rouge\">readObject</code> 和 <code class=\"language-plaintext highlighter-rouge\">writeObject</code> 调用使用新的对象、对象特征和字符串的隐式引用表。</p>\n\n<h3 id=\"二binaryreaderwriter\">二、<code class=\"language-plaintext highlighter-rouge\">BinaryReader/Writer</code></h3>\n\n<h4 id=\"1amf3-数据格式基础\">1、AMF3 数据格式基础</h4>\n\n<p>首先介绍一下变长整数(Variable Length Integer),比如 UInt32 如下。</p>\n\n<p><img src=\"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png\" alt=\"image\" /></p>\n\n<p>上图摘自 Adobe AMF3 官方文档,这是一种压缩方式的整数存储,且每一字节都对后面的数据具有预知作用。那么字符串如何处理呢?下面是字符串的处理方式,AMF0 和 AMF3 都才用 UTF-8 编码方式,并做如下压缩处理:</p>\n\n<p><img src=\"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png\" alt=\"image\" /></p>\n\n<p>上图摘自 Adobe AMF3 官方文档。</p>\n\n<h4 id=\"2序列化\">2、序列化</h4>\n\n<p>序列化包括 8 位、16 位、32 位,以及 UTF-8 和 UTF-16(I guess)编码的 String,还有原生数据(Raw Data)、变长无符号整数(Variable Length Unsigned Integer)以及 IP 地址。所谓序列化就是按照指定格式编写各种对象、基础数据类型值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">BinaryWriter</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ostream</span><span class=\"o\">&</span> <span class=\"n\">ostr</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">BinaryWriter</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write7BitValue</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">write7BitLongValue</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt64</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeAddress</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Address</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">,</span><span class=\"kt\">bool</span> <span class=\"n\">publicFlag</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">writeAddress</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Net</span><span class=\"o\">::</span><span class=\"n\">SocketAddress</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">,</span><span class=\"kt\">bool</span> <span class=\"n\">publicFlag</span><span class=\"p\">);</span>\n <span class=\"k\">static</span> <span class=\"n\">BinaryWriter</span> <span class=\"n\">BinaryWriterNull</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>请注意其中名为 <code class=\"language-plaintext highlighter-rouge\">BinaryWriterNull</code> 的成员。构造函数定义为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">ostream</span><span class=\"o\">&</span> <span class=\"n\">ostr</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">ostr</span><span class=\"p\">,</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">NETWORK_BYTE_ORDER</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n\n<span class=\"n\">BinaryWriter</span><span class=\"o\">::~</span><span class=\"n\">BinaryWriter</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">writeRaw</code> 是简单地封装 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter::writeRaw()</code>,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入整数实现如下,用的是从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 继承来的重载运算符操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span> \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入字符串:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入变长整数,这段代码含义也一目了然,就是读取变长无符号 32 位整数、64 位整数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write7BitValue</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">Get7BitValueSize</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span><span class=\"o\">*</span><span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">21</span><span class=\"p\">)</span> <span class=\"p\">{</span> <span class=\"c1\">// 4 bytes maximum</span>\n <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"mi\">22</span><span class=\"p\">;</span>\n <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">7</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"mh\">0x80</span> <span class=\"o\">|</span> <span class=\"p\">((</span><span class=\"n\">value</span><span class=\"o\">>></span><span class=\"n\">shift</span><span class=\"p\">)</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">));</span>\n <span class=\"n\">shift</span> <span class=\"o\">-=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">max</span> <span class=\"o\">?</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0xFF</span> <span class=\"o\">:</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write7BitLongValue</span><span class=\"p\">(</span><span class=\"n\">UInt64</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">shift</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">Util</span><span class=\"o\">::</span><span class=\"n\">Get7BitValueSize</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span><span class=\"o\">-</span><span class=\"mi\">1</span><span class=\"p\">)</span><span class=\"o\">*</span><span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">max</span> <span class=\"o\">=</span> <span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">63</span><span class=\"p\">;</span> <span class=\"c1\">// Can give 10 bytes!</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">max</span><span class=\"p\">)</span>\n <span class=\"o\">++</span><span class=\"n\">shift</span><span class=\"p\">;</span>\n \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">shift</span><span class=\"o\">>=</span><span class=\"mi\">7</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"mh\">0x80</span> <span class=\"o\">|</span> <span class=\"p\">((</span><span class=\"n\">value</span><span class=\"o\">>></span><span class=\"n\">shift</span><span class=\"p\">)</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">));</span>\n <span class=\"n\">shift</span> <span class=\"o\">-=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">max</span> <span class=\"o\">?</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0xFF</span> <span class=\"o\">:</span> <span class=\"n\">value</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写入 IP 地址的两个函数暂略。</p>\n\n<h4 id=\"3反序列化\">3、反序列化</h4>\n\n<p>反序列化就是从指定格式的数据中读出各类型的数据值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">BinaryReader</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">istream</span><span class=\"o\">&</span> <span class=\"n\">istr</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">BinaryReader</span><span class=\"p\">();</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt64</span> <span class=\"n\">read7BitLongValue</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read7BitEncoded</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString8</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString16</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">read16</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">read32</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readAddress</span><span class=\"p\">(</span><span class=\"n\">Address</span><span class=\"o\">&</span> <span class=\"n\">address</span><span class=\"p\">);</span>\n \n <span class=\"k\">static</span> <span class=\"n\">BinaryReader</span> <span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>构造与析构函数都很简单:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">istream</span><span class=\"o\">&</span> <span class=\"n\">istr</span><span class=\"p\">)</span> <span class=\"o\">:</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">istr</span><span class=\"p\">,</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">NETWORK_BYTE_ORDER</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">BinaryReader</span><span class=\"o\">::~</span><span class=\"n\">BinaryReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读取原生数据(Raw Data):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">readRaw</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">,</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写整数,用的是 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter</code> 的重载运算符:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt16</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">write32</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\"><<</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读写整数依旧使用从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 继承来的运算符操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt8</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read8</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">UInt16</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read16</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">UInt32</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read32</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"o\">>></span> <span class=\"n\">c</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">c</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>写字符串:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString8</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write8</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">UInt16</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">,</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n<span class=\"kt\">void</span> <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">writeString16</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">write16</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">());</span>\n <span class=\"n\">writeRaw</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读取变长整数,分别针对 <code class=\"language-plaintext highlighter-rouge\">UInt32</code> 和 <code class=\"language-plaintext highlighter-rouge\">UInt64</code>,要理解 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 的变长整数才能理解这个:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x80</span><span class=\"p\">)</span> <span class=\"o\">&&</span> <span class=\"n\">n</span> <span class=\"o\"><</span> <span class=\"mi\">3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"p\">(</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"o\">++</span><span class=\"n\">n</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"p\">((</span><span class=\"n\">n</span><span class=\"o\"><</span><span class=\"mi\">3</span><span class=\"p\">)</span> <span class=\"o\">?</span> <span class=\"mi\">7</span> <span class=\"o\">:</span> <span class=\"mi\">8</span><span class=\"p\">);</span> <span class=\"c1\">// Use all 8 bits from the 4th byte</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"n\">b</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt64</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">read7BitLongValue</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"n\">UInt64</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">while</span> <span class=\"p\">((</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x80</span><span class=\"p\">)</span> <span class=\"o\">&&</span> <span class=\"n\">n</span> <span class=\"o\"><</span> <span class=\"mi\">8</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"mi\">7</span><span class=\"p\">;</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"p\">(</span><span class=\"n\">b</span><span class=\"o\">&</span><span class=\"mh\">0x7F</span><span class=\"p\">);</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">read8</span><span class=\"p\">();</span>\n <span class=\"o\">++</span><span class=\"n\">n</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">result</span> <span class=\"o\"><<=</span> <span class=\"p\">((</span><span class=\"n\">n</span><span class=\"o\"><</span><span class=\"mi\">8</span><span class=\"p\">)</span> <span class=\"o\">?</span> <span class=\"mi\">7</span> <span class=\"o\">:</span> <span class=\"mi\">8</span><span class=\"p\">);</span> <span class=\"c1\">// Use all 8 bits from the 4th byte</span>\n <span class=\"n\">result</span> <span class=\"o\">|=</span> <span class=\"n\">b</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三packetreaderwriter\">三、<code class=\"language-plaintext highlighter-rouge\">PacketReader/Writer</code></h3>\n\n<h4 id=\"1packetreader\">1、PacketReader</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>#define PACKETRECV_SIZE 2048\nclass PacketReader: public BinaryReader {\npublic:\n PacketReader(const Poco::UInt8* buffer,Poco::UInt32 size);\n PacketReader(PacketReader&);\n virtual ~PacketReader();\n const Poco::UInt32 fragments;\n Poco::UInt32 available(); // 可读字节数\n Poco::UInt8* current();\n Poco::UInt32 position(); // 获取当前的相对位置(相对于起始位置的)\n void reset(Poco::UInt32 newPos = 0); // 设定当前位置\n void shrink(Poco::UInt32 rest);\n void next(Poco::UInt32 size);\nprivate:\n MemoryInputStream _memory;\n};\n</code></pre></div></div>\n\n<h6 id=\"11封装-memoryinputstream\">1.1、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">current</code>:当前绝对位置(内存地址)</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code>:当前位置(绝对位置)减去缓冲区起始位置</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"12收缩缓冲区\">1.2、收缩缓冲区</h6>\n\n<p>封装了 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 的 <code class=\"language-plaintext highlighter-rouge\">resize</code>。不过由于前面的 <code class=\"language-plaintext highlighter-rouge\">if</code> 语句影响,传给 resize 的参数一定不会大于缓冲区的当前大小。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">shrink</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">rest</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">rest</span> <span class=\"o\">></span> <span class=\"n\">available</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"rest %u more upper than available %u bytes\"</span><span class=\"p\">,</span><span class=\"n\">rest</span><span class=\"p\">,</span><span class=\"n\">available</span><span class=\"p\">());</span>\n <span class=\"n\">rest</span> <span class=\"o\">=</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">rest</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"13构造函数拷贝构造函数和析构函数\">1.3、构造函数、拷贝构造函数和析构函数</h6>\n\n<p>构造函数先调用父类 <code class=\"language-plaintext highlighter-rouge\">BinaryReader</code> 的构造函数,并初始化 <code class=\"language-plaintext highlighter-rouge\">fragments</code> 和 <code class=\"language-plaintext highlighter-rouge\">_memory</code> 输入流的缓冲区。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">PacketReader</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">((</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">fragments</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"c1\">// Consctruction by copy</span>\n<span class=\"n\">PacketReader</span><span class=\"o\">::</span><span class=\"n\">PacketReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryReader</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">fragments</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">fragments</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">PacketReader</span><span class=\"o\">::~</span><span class=\"n\">PacketReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2packetwriter\">2、<code class=\"language-plaintext highlighter-rouge\">PacketWriter</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">PacketWriter</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">BinaryWriter</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"n\">PacketWriter</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"o\">~</span><span class=\"n\">PacketWriter</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">good</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">clear</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">limit</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"n\">MemoryOutputStream</span> <span class=\"n\">_memory</span><span class=\"p\">;</span>\n <span class=\"n\">PacketWriter</span><span class=\"o\">*</span> <span class=\"n\">_pOther</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h6 id=\"21封装memoryoutputstream\">2.1、封装<code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">good</code>:不过 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code> 也是封装的 <code class=\"language-plaintext highlighter-rouge\">std::ostream</code> 的 <code class=\"language-plaintext highlighter-rouge\">good</code> 函数,True if no error flags are set.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">bool</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">good</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">good</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">written</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">length</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code>:设置缓冲区的指针位置,即 <code class=\"language-plaintext highlighter-rouge\">position</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code>:移动缓冲区指针</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin</code>:返回缓冲区的起始地址</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">clear</code>:其实就是修改 written 和 position,使得指定位置后面的数据在以后写的时候可以被覆盖,并不是真正的清除。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">clear</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">limit</code>:封装 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code> 的 <code class=\"language-plaintext highlighter-rouge\">resize</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">limit</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">length</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">length</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">length</span> <span class=\"o\">=</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">length</span> <span class=\"o\">></span> <span class=\"n\">_size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"Limit '%d' more upper than buffer size '%d' bytes\"</span><span class=\"p\">,</span><span class=\"n\">length</span><span class=\"p\">,</span><span class=\"n\">_size</span><span class=\"p\">);</span>\n <span class=\"n\">length</span> <span class=\"o\">=</span> <span class=\"n\">_size</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">length</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"22封装-binarywriter\">2.2、封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code></h6>\n\n<p><code class=\"language-plaintext highlighter-rouge\">flush</code>:封装 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code> 的 <code class=\"language-plaintext highlighter-rouge\">flush</code>,不过 <code class=\"language-plaintext highlighter-rouge\">BinaryWriter</code> 的 <code class=\"language-plaintext highlighter-rouge\">flush</code> 实际上是从 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryWriter</code> 继承而来的。其作用是「Flushes the underlying stream」。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">flush</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_pOther</span> <span class=\"o\">&&</span> <span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"n\">_pOther</span><span class=\"o\">-></span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">())</span>\n <span class=\"n\">_pOther</span><span class=\"o\">-></span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">.</span><span class=\"n\">written</span><span class=\"p\">());</span>\n <span class=\"n\">BinaryWriter</span><span class=\"o\">::</span><span class=\"n\">flush</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h6 id=\"23构造函数拷贝构造函数和析构函数\">2.3、构造函数、拷贝构造函数和析构函数</h6>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">UInt8</span><span class=\"o\">*</span> <span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_memory</span><span class=\"p\">((</span><span class=\"kt\">char</span><span class=\"o\">*</span><span class=\"p\">)</span><span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">_pOther</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_size</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"c1\">// Consctruction by copy</span>\n<span class=\"n\">PacketWriter</span><span class=\"o\">::</span><span class=\"n\">PacketWriter</span><span class=\"p\">(</span><span class=\"n\">PacketWriter</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_pOther</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">other</span><span class=\"p\">),</span>\n <span class=\"n\">_memory</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">BinaryWriter</span><span class=\"p\">(</span><span class=\"n\">_memory</span><span class=\"p\">),</span>\n <span class=\"n\">_size</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>注意析构函数中会进行 <code class=\"language-plaintext highlighter-rouge\">flush</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">PacketWriter</span><span class=\"o\">::~</span><span class=\"n\">PacketWriter</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">flush</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"四amfreader\">四、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code></h3>\n\n<h4 id=\"1objectdef\">1、<code class=\"language-plaintext highlighter-rouge\">ObjectDef</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ObjectDef</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span> \n <span class=\"n\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">amf3</span><span class=\"p\">(</span><span class=\"n\">amf3</span><span class=\"p\">),</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">dynamic</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">externalizable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">count</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">arrayType</span><span class=\"p\">(</span><span class=\"n\">arrayType</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">string</span><span class=\"o\">></span> <span class=\"n\">hardProperties</span><span class=\"p\">;</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">reset</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">dynamic</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">externalizable</span><span class=\"p\">;</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">count</span><span class=\"p\">;</span>\n <span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"p\">;</span>\n <span class=\"k\">const</span> <span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"2amfreader-定义\">2、<code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 定义</h4>\n\n<p>其中 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 作为其成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">AMFReader</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">AMFReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">AMFReader</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">readSimpleObject</span><span class=\"p\">(</span><span class=\"n\">AMFSimpleObject</span><span class=\"o\">&</span> <span class=\"n\">object</span><span class=\"p\">);</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">read</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"kt\">double</span> <span class=\"n\">readNumber</span><span class=\"p\">();</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Int32</span> <span class=\"n\">readInteger</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readBoolean</span><span class=\"p\">();</span>\n <span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">readByteArray</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">&</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Timestamp</span> <span class=\"n\">readDate</span><span class=\"p\">();</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">readObject</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readArray</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">readDictionary</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">weakKeys</span><span class=\"p\">);</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readKey</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readValue</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">readItem</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">);</span>\n <span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">readRawObjectContent</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">readNull</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n \n <span class=\"kt\">bool</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">startReferencing</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">stopReferencing</span><span class=\"p\">();</span>\n \n <span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">;</span>\n \n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">readString</span><span class=\"p\">(</span><span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">value</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">ObjectDef</span><span class=\"o\">*></span> <span class=\"n\">_objectDefs</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_stringReferences</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_classDefReferences</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">;</span>\n <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">vector</span><span class=\"o\"><</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span><span class=\"o\">></span> <span class=\"n\">_amf0References</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_amf0Reset</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_reset</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_amf3</span><span class=\"p\">;</span>\n <span class=\"kt\">bool</span> <span class=\"n\">_referencing</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"21构造函数析构函数\">2.1、构造函数、析构函数</h5>\n\n<p>参数为 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code>,会初始化一些成员变量。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">AMFReader</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">reader</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">reader</span><span class=\"p\">(</span><span class=\"n\">reader</span><span class=\"p\">),</span>\n <span class=\"n\">_reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_amf3</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_amf0Reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">_referencing</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构时,会逐一释放 <code class=\"language-plaintext highlighter-rouge\">_objectDefs</code> 中对象的内存:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMFReader</span><span class=\"o\">::~</span><span class=\"n\">AMFReader</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">list</span><span class=\"o\"><</span><span class=\"n\">ObjectDef</span><span class=\"o\">*>::</span><span class=\"n\">iterator</span> <span class=\"n\">it</span><span class=\"p\">;</span>\n <span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span>\n <span class=\"k\">delete</span> <span class=\"o\">*</span><span class=\"n\">it</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22简单封装-packetreader-的一些函数\">2.2、简单封装 <code class=\"language-plaintext highlighter-rouge\">PacketReader</code> 的一些函数</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code>:操作指针位置</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_reset</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"p\">);</span>\n <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">available</code>:根据当前缓冲区大小和 <code class=\"language-plaintext highlighter-rouge\">written</code> 计算得到</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">current</code>:<code class=\"language-plaintext highlighter-rouge\">gptr</code> 内存地址</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt8</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">*</span><span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23设置-gptr-位置\">2.3、设置 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 位置</h5>\n\n<p>其实 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 也被影响了,但是在 <code class=\"language-plaintext highlighter-rouge\">AMFReader</code> 中只用 <code class=\"language-plaintext highlighter-rouge\">gptr</code>。调用构造函数的时候,<code class=\"language-plaintext highlighter-rouge\">reset</code> 被设为 0,其后在每次读取数据的时候都会影响 <code class=\"language-plaintext highlighter-rouge\">reset</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_reset</span><span class=\"p\">);</span>\n <span class=\"n\">_reset</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"24判断类型\">2.4、判断类型</h5>\n\n<p>分析请看注释:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">followingType</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先 <code class=\"language-plaintext highlighter-rouge\">reset</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span> <span class=\"o\">!=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"o\">></span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">back</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">amf3</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>是 AMF0 类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>如果没有可读数据了,则返回 AMF::End。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">available</span><span class=\"p\">())</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">End</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>开始读了,先读到的表示 AMF 数据类型。要注意的是调用 current 并不改变指针的位置,所以你会在线面看到调用 next。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt8</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n \n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_amf3</span> <span class=\"o\">&&</span> <span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF_AVMPLUS_OBJECT</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"n\">_amf3</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">available</span><span class=\"p\">())</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">End</span><span class=\"p\">;</span>\n <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>AMF3 类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">switch</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>Undefined 和 null 都当做 null。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF3_UNDEFINED</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_NULL</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>false 和 true 都是 boolean。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF3_FALSE</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_TRUE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_INTEGER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Integer</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_NUMBER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">String</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_DATE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Array</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_DICTIONARY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Dictionary</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Object</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF3_BYTEARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">ByteArray</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>落到 default 手里的话,就跳过这个字节,读取下一个。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"nl\">default:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Unknown AMF3 type %.2x\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>AMF0 类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">switch</span> <span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">undefined</code> 和 <code class=\"language-plaintext highlighter-rouge\">null</code> 都是 <code class=\"language-plaintext highlighter-rouge\">null</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_UNDEFINED</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_NULL</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">;</span>\n \n <span class=\"k\">case</span> <span class=\"n\">AMF_BOOLEAN</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_NUMBER</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">long string</code> 和 <code class=\"language-plaintext highlighter-rouge\">string</code> 都是 <code class=\"language-plaintext highlighter-rouge\">string</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_LONG_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_STRING</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">String</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">mixed array</code> 和 <code class=\"language-plaintext highlighter-rouge\">strict array</code> 都是 <code class=\"language-plaintext highlighter-rouge\">array</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_MIXED_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_STRICT_ARRAY</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Array</span><span class=\"p\">;</span>\n \n <span class=\"k\">case</span> <span class=\"n\">AMF_DATE</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin object</code> 和 <code class=\"language-plaintext highlighter-rouge\">begin typed object</code> 都是 <code class=\"language-plaintext highlighter-rouge\">object</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_BEGIN_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_BEGIN_TYPED_OBJECT</span><span class=\"p\">:</span>\n <span class=\"k\">return</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Object</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是引用,就跳过表示类型值的这个字节。这个先留下来,带我们分析完 readArray 和 readObject 再回头看。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_REFERENCE</span><span class=\"p\">:</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"n\">UInt16</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read16</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">reference</span> <span class=\"o\">></span> <span class=\"n\">_amf0References</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF0 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_amf0Reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_amf0References</span><span class=\"p\">[</span><span class=\"n\">reference</span><span class=\"p\">]);</span>\n <span class=\"k\">return</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果没了,或者不支持,或者都不是,就跳过这个字节,递归继续读取:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">case</span> <span class=\"n\">AMF_END_OBJECT</span><span class=\"p\">:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF end object type without begin object type before\"</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">case</span> <span class=\"n\">AMF_UNSUPPORTED</span><span class=\"p\">:</span>\n <span class=\"n\">WARN</span><span class=\"p\">(</span><span class=\"s\">\"Unsupported type in AMF format\"</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">default</span><span class=\"o\">:</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Unknown AMF type %.2x\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">)</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nf\">followingType</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">followingType</code> 是这个类的核心,每个具体的数据类型的分析都依赖于它的判断。这些类型的解析,会在下一篇文章中介绍。</p>\n\n<h4 id=\"3解析-as3-null\">3、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Null</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readNull</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先 reset 一下是惯例,就像糗百上的割一样。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span> \n</code></pre></div></div>\n\n<p>AMF 数据类型</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">Null</code>,跳过该字节,并返回</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>判断错误</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Null type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4解析-as3-number\">4、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Number</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">double</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readNumber</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Null</code> 会被悲催的跳过:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">Number</code> 呀 :(</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Number type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过该字节吧</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>返回吧,返回之前还用到 <code class=\"language-plaintext highlighter-rouge\">Poco::BinaryReader</code> 的运算符,注意 AS3 中的 <code class=\"language-plaintext highlighter-rouge\">Number</code> 就是 C++ 的 <code class=\"language-plaintext highlighter-rouge\">double</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">double</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"5解析-as3-integer\">5、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Integer</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Int32</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readInteger</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">reset</code> 类型:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>Null 的话:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">Integer</code> 或者 Number 的话。。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Integer</span> <span class=\"o\">&&</span> <span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Integer type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过吧。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>终于是 <code class=\"language-plaintext highlighter-rouge\">Number</code> 了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Number</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kt\">double</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">Int32</span><span class=\"p\">)</span><span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>读一个变长的 32 位无符号整数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// Forced in AMF3 here!</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">value</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果大于 3.5 个字节所能表示的最大无符号整数值(<code class=\"language-plaintext highlighter-rouge\">268435455</code> 是 <code class=\"language-plaintext highlighter-rouge\">0xFFFFFFF</code>),则减去 <code class=\"language-plaintext highlighter-rouge\">0x2FFFFFFF</code>(这还不是太理解,有能解释的朋友给留个言,或者发 email 给我 )</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">value</span> <span class=\"o\">></span> <span class=\"mi\">268435455</span><span class=\"p\">)</span>\n <span class=\"n\">value</span> <span class=\"o\">-=</span> <span class=\"p\">(</span><span class=\"mi\">1</span> <span class=\"o\"><<</span> <span class=\"mi\">29</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">value</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"6解析-as3-boolean\">6、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Boolean</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readBoolean</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">Null</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>居然不是 <code class=\"language-plaintext highlighter-rouge\">Boolean</code> 的话。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Boolean</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Boolean type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果是 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 的话,返回 <code class=\"language-plaintext highlighter-rouge\">true</code> 或者 <code class=\"language-plaintext highlighter-rouge\">false</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span><span class=\"o\">==</span> <span class=\"n\">AMF3_FALSE</span> <span class=\"o\">?</span> <span class=\"nb\">false</span> <span class=\"o\">:</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>不是 <code class=\"language-plaintext highlighter-rouge\">AMF3</code> 就是 <code class=\"language-plaintext highlighter-rouge\">AMF0</code> 喽:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span><span class=\"o\">==</span><span class=\"mh\">0x00</span> <span class=\"o\">?</span> <span class=\"nb\">false</span> <span class=\"o\">:</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"7开始引用与结束引用\">7、开始引用与结束引用</h4>\n\n<p>如下这两个函数会在 <code class=\"language-plaintext highlighter-rouge\">FlowConnection</code> 中调用。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">startReferencing</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_referencing</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">stopReferencing</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_referencing</span> <span class=\"o\">=</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"8解析-as3-bytearray\">8、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code></h4>\n\n<p>先回顾一下 AMF3 中的ByteArray 的数据格式:</p>\n\n<p>注意到,首先要读取一个变长无符号 32 位整数,但是最低位是 1,只有 28 位用于表示数据长度。解释完这里,下面的解析过程才好理解。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">BinaryReader</span><span class=\"o\">&</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readByteArray</span><span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"o\">&</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Null</code> 就返回 <code class=\"language-plaintext highlighter-rouge\">BinaryReaderNull</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>,也返回 <code class=\"language-plaintext highlighter-rouge\">BinaryReaderNull</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">ByteArray</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF ByteArray type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过这个字节:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>注意 position 返回的是相对位置,与 AS3 中一样。<code class=\"language-plaintext highlighter-rouge\">reference</code> 表示这个地址(简单说,引用就是地址嘛)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>读取一个变长 32 位无符号整数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>最低位是 1 的话,<code class=\"language-plaintext highlighter-rouge\">isInline</code> 是 <code class=\"language-plaintext highlighter-rouge\">true</code>,否则为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">size</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>右移一位,因为那一位是标志位,上面解释过了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">isInline</code> 是 <code class=\"language-plaintext highlighter-rouge\">true</code>,表示是 <code class=\"language-plaintext highlighter-rouge\">ByteArray</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>如果 <code class=\"language-plaintext highlighter-rouge\">_referencing</code> 为 <code class=\"language-plaintext highlighter-rouge\">true</code> 的话(这是一个 <code class=\"language-plaintext highlighter-rouge\">vector</code>),push back this reference:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>不符合 ByteArray 的格式定义的话:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">size</span> <span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">BinaryReader</span><span class=\"o\">::</span><span class=\"n\">BinaryReaderNull</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>移动到这个 reference 的位置,<code class=\"language-plaintext highlighter-rouge\">_references[size]</code> 就是这个位置(相对)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">size</span><span class=\"p\">]);</span> <span class=\"c1\">// TODO size 作为索引,还没有完全理解</span>\n</code></pre></div></div>\n\n<p>读取这个 reference 的 size 值给 size对象(注意 size 是这个函数传入的引用参数,其值可以被修改)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"o\">>></span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>把读取完 ByteArraty 的 PacketReader 返回:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"n\">reader</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>最后强调一点,<code class=\"language-plaintext highlighter-rouge\">ByteArray</code> 的数据段最大长度为 228 -1 字节,约为 256 MB。</p>\n\n<h4 id=\"9解析-as3-date\">9、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Date</code></h4>\n\n<p>先看下 <code class=\"language-plaintext highlighter-rouge\">Date</code> 的数据格式:</p>\n\n<p>下面开始分析:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Timestamp</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readDate</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>惯例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>Null 的话,就返回当前时间:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果不是 Date 类型,也返回当前时间:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Date</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Date type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"kt\">double</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果是 AMF3:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>先读取 flag,最低一位必须是 1,其他位丢到垃圾桶。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">flags</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>当前相对位置。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>是 1 就 push back 到 <code class=\"language-plaintext highlighter-rouge\">_references</code> 里。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">flags</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>读取一个 double,到 result 里(result 也是 double 类型哦~)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>如果标志位不是 1,麻烦不少哒。。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">flags</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>如果 flag 超了,就返回当前时间作为时间戳作为 Date。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">flags</span> <span class=\"o\">></span> <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">Timestamp</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这段与 ByteArray 那段一样:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">_reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">flags</span><span class=\"p\">]);</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>返回喽~</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"nf\">Timestamp</span><span class=\"p\">((</span><span class=\"n\">Timestamp</span><span class=\"o\">::</span><span class=\"n\">TimeVal</span><span class=\"p\">)</span> <span class=\"n\">result</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">);</span>\n <span class=\"err\">}</span>\n <span class=\"n\">reader</span> <span class=\"o\">>></span> <span class=\"n\">result</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>读俩,因为是 double(64 位):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">2</span><span class=\"p\">);</span> <span class=\"c1\">// Timezone, useless</span>\n</code></pre></div></div>\n\n<p>返回喽~</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">return</span> <span class=\"nf\">Timestamp</span><span class=\"p\">((</span><span class=\"n\">Timestamp</span><span class=\"o\">::</span><span class=\"n\">TimeVal</span><span class=\"p\">)</span> <span class=\"n\">result</span> <span class=\"o\">*</span> <span class=\"mi\">1000</span><span class=\"p\">);</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"10解析-as3-dictionary\">10、解析 AS3 <code class=\"language-plaintext highlighter-rouge\">Dictionary</code></h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">bool</span> <span class=\"n\">AMFReader</span><span class=\"o\">::</span><span class=\"n\">readDictionary</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">weakKeys</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n</code></pre></div></div>\n\n<p>下面这段咱就略了。。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">reset</span><span class=\"p\">();</span>\n <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Type</span> <span class=\"n\">type</span> <span class=\"o\">=</span> <span class=\"n\">followingType</span><span class=\"p\">();</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">==</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Null</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">type</span> <span class=\"o\">!=</span> <span class=\"n\">AMF</span><span class=\"o\">::</span><span class=\"n\">Dictionary</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"Type %.2x is not a AMF Dictionary type\"</span><span class=\"p\">,</span><span class=\"n\">type</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>跳过 type:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"c1\">// AMF3</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">);</span> <span class=\"c1\">// marker</span>\n</code></pre></div></div>\n\n<p>当前相对位置值作为 <code class=\"language-plaintext highlighter-rouge\">reference</code>,再读个 <code class=\"language-plaintext highlighter-rouge\">size</code>,还是最低位必须为 1,不是就返回 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">UInt32</span> <span class=\"n\">reference</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">UInt32</span> <span class=\"n\">size</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">();</span>\n <span class=\"kt\">bool</span> <span class=\"n\">isInline</span> <span class=\"o\">=</span> <span class=\"n\">size</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n <span class=\"n\">size</span> <span class=\"o\">>>=</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">isInline</span> <span class=\"o\">&&</span> <span class=\"n\">size</span><span class=\"o\">></span><span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">size</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span><span class=\"s\">\"AMF3 reference not found\"</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>下面要调用到 <code class=\"language-plaintext highlighter-rouge\">ObjectRef</code> 构造函数,这里再把其实现拿出来看看,其实主要是初始化了哪些成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">amf3</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">arrayType</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">amf3</span><span class=\"p\">(</span><span class=\"n\">amf3</span><span class=\"p\">),</span>\n <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">dynamic</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">externalizable</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">count</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">),</span>\n <span class=\"n\">arrayType</span><span class=\"p\">(</span><span class=\"n\">arrayType</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>可以看到要有一个 amf3,还有 <code class=\"language-plaintext highlighter-rouge\">reset</code> 置为 0,<code class=\"language-plaintext highlighter-rouge\">dynamic</code> 置为 <code class=\"language-plaintext highlighter-rouge\">false</code>,<code class=\"language-plaintext highlighter-rouge\">externalizable</code> 也是 <code class=\"language-plaintext highlighter-rouge\">false</code>,<code class=\"language-plaintext highlighter-rouge\">count</code> 是 0,<code class=\"language-plaintext highlighter-rouge\">arrayType</code> 成员要赋值。</p>\n\n<p>上面是插播哦,下面还要继续哒。创建这么一个对象,注意是 new 出来的,所以我们在《OpenRTMFP/Cumulus Primer(16)AMF解析之AMFReader》一文中提到了 AMFReader 的析构函数中要对 <code class=\"language-plaintext highlighter-rouge\">_objectRef</code> 的每个元素逐一析构的。<code class=\"language-plaintext highlighter-rouge\">arrayType</code> 就设置为 <code class=\"language-plaintext highlighter-rouge\">AMF3_DICTIONARY</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">ObjectDef</span><span class=\"o\">*</span> <span class=\"n\">pObjectDef</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"nf\">ObjectDef</span><span class=\"p\">(</span><span class=\"n\">_amf3</span><span class=\"p\">,</span> <span class=\"n\">AMF3_DICTIONARY</span><span class=\"p\">);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">dynamic</span><span class=\"o\">=</span><span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"n\">_objectDefs</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pObjectDef</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>如果标志位是 1,就直接 push back,跟之前一样。不过这里多了一个 <code class=\"language-plaintext highlighter-rouge\">pObjectDef</code>,所以还要设置一下它的计数为 <code class=\"language-plaintext highlighter-rouge\">size</code>,就是 <code class=\"language-plaintext highlighter-rouge\">dictionary</code> 数据大小。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">isInline</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_referencing</span><span class=\"p\">)</span>\n <span class=\"n\">_references</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">reference</span><span class=\"p\">);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">=</span> <span class=\"n\">size</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>如果标志位是 0,就把 <code class=\"language-plaintext highlighter-rouge\">count</code> 设置为下一个变长整数值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">reset</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">position</span><span class=\"p\">();</span>\n <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">_references</span><span class=\"p\">[</span><span class=\"n\">size</span><span class=\"p\">]);</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read7BitValue</span><span class=\"p\">()</span> <span class=\"o\">>></span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"n\">pObjectDef</span><span class=\"o\">-></span><span class=\"n\">count</span> <span class=\"o\">*=</span> <span class=\"mi\">2</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>读一个字节,如果最小位是 1,weakKeys 就是 true,否则为 false。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"n\">weakKeys</span> <span class=\"o\">=</span> <span class=\"n\">reader</span><span class=\"p\">.</span><span class=\"n\">read8</span><span class=\"p\">()</span> <span class=\"o\">&</span> <span class=\"mh\">0x01</span><span class=\"p\">;</span>\n \n <span class=\"k\">return</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</h2>\t\t\n\t<time datetime=\"2012-04-24T03:31:10+00:00\" class=\"by-line\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 12668 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一流缓冲区\" id=\"markdown-toc-一流缓冲区\">一、流缓冲区</a> <ul>\n <li><a href=\"#1了解-stdstreambuf\" id=\"markdown-toc-1了解-stdstreambuf\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::streambuf</code></a> <ul>\n <li><a href=\"#11单步移动内置指针\" id=\"markdown-toc-11单步移动内置指针\">1.1、单步移动内置指针</a></li>\n <li><a href=\"#12获取-get-指针和-put-指针的位置\" id=\"markdown-toc-12获取-get-指针和-put-指针的位置\">1.2、获取 get 指针和 put 指针的位置</a></li>\n <li><a href=\"#13设置-get-和-put-指针可达区域的上下界\" id=\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\">1.3、设置 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针可达区域的上下界</a></li>\n </ul>\n </li>\n <li><a href=\"#2memorystreambuf\" id=\"markdown-toc-2memorystreambuf\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code></a> <ul>\n <li><a href=\"#21移动内置的-get-和-put-指针\" id=\"markdown-toc-21移动内置的-get-和-put-指针\">2.1、移动内置的 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针:</a></li>\n <li><a href=\"#22获取-get-和-put-指针当前位置\" id=\"markdown-toc-22获取-get-和-put-指针当前位置\">2.2、获取 get 和 put 指针当前位置:</a></li>\n <li><a href=\"#23获取缓冲区的起始位置和大小\" id=\"markdown-toc-23获取缓冲区的起始位置和大小\">2.3、获取缓冲区的起始位置和大小:</a></li>\n <li><a href=\"#24缓冲区的已写字节数\" id=\"markdown-toc-24缓冲区的已写字节数\">2.4、缓冲区的已写字节数</a></li>\n <li><a href=\"#25显式设定-put-和-get-指针位置\" id=\"markdown-toc-25显式设定-put-和-get-指针位置\">2.5、显式设定 <code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针位置</a></li>\n <li><a href=\"#26-修改缓冲区大小\" id=\"markdown-toc-26-修改缓冲区大小\">2.6 修改缓冲区大小</a></li>\n <li><a href=\"#27构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-27构造函数拷贝构造函数和析构函数\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#二io-流\" id=\"markdown-toc-二io-流\">二、IO 流</a> <ul>\n <li><a href=\"#1了解-stdios\" id=\"markdown-toc-1了解-stdios\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::ios</code></a></li>\n <li><a href=\"#2memoryios\" id=\"markdown-toc-2memoryios\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code></a> <ul>\n <li><a href=\"#21构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-21构造函数拷贝构造函数和析构函数\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\n <li><a href=\"#22得到-memorystreambuf-成员的地址\" id=\"markdown-toc-22得到-memorystreambuf-成员的地址\">2.2、得到 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的地址</a></li>\n <li><a href=\"#23当前位置\" id=\"markdown-toc-23当前位置\">2.3、当前位置</a></li>\n <li><a href=\"#24封装-memorystreambuf-成员的一些函数\" id=\"markdown-toc-24封装-memorystreambuf-成员的一些函数\">2.4、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的一些函数</a></li>\n <li><a href=\"#25-缓冲区可读数据的字节数\" id=\"markdown-toc-25-缓冲区可读数据的字节数\">2.5 缓冲区可读数据的字节数</a></li>\n </ul>\n </li>\n <li><a href=\"#3输入流\" id=\"markdown-toc-3输入流\">3、输入流</a></li>\n <li><a href=\"#4输出流\" id=\"markdown-toc-4输出流\">4、输出流</a> <ul>\n <li><a href=\"#41-构造函数拷贝构造函数和析构函数\" id=\"markdown-toc-41-构造函数拷贝构造函数和析构函数\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\n <li><a href=\"#42-读取和设定已写字节数\" id=\"markdown-toc-42-读取和设定已写字节数\">4.2 读取和设定已写字节数</a></li>\n <li><a href=\"#43-当前位置\" id=\"markdown-toc-43-当前位置\">4.3 当前位置</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#三局部内存片\" id=\"markdown-toc-三局部内存片\">三、局部内存片</a> <ul>\n <li><a href=\"#1构造函数\" id=\"markdown-toc-1构造函数\">1、构造函数</a></li>\n <li><a href=\"#2析构函数\" id=\"markdown-toc-2析构函数\">2、析构函数</a></li>\n <li><a href=\"#3缓冲区切割\" id=\"markdown-toc-3缓冲区切割\">3、缓冲区切割</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\n\n<h3 id=\"一流缓冲区\">一、流缓冲区</h3>\n\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\n\n<h4 id=\"1了解-stdstreambuf\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::streambuf</code></h4>\n\n<p>首先要了解 <code class=\"language-plaintext highlighter-rouge\">streambuf</code> 内置了一个 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针和一个 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针。<code class=\"language-plaintext highlighter-rouge\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\"language-plaintext highlighter-rouge\">g</code> 和 <code class=\"language-plaintext highlighter-rouge\">p</code> 就分别表示 get pointer 和 put pointer。</p>\n\n<h5 id=\"11单步移动内置指针\">1.1、单步移动内置指针</h5>\n\n<p>Increase get pointer: Advances the get pointer by <code class=\"language-plaintext highlighter-rouge\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">gbump</span> <span class=\"p\">(</span> <span class=\"kt\">int</span> <span class=\"n\">n</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>Increase put pointer: Advances the put pointer by <code class=\"language-plaintext highlighter-rouge\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">pbump</span> <span class=\"p\">(</span> <span class=\"kt\">int</span> <span class=\"n\">n</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<h5 id=\"12获取-get-指针和-put-指针的位置\">1.2、获取 get 指针和 put 指针的位置</h5>\n\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">char</span> <span class=\"o\">*</span> <span class=\"n\">gptr</span> <span class=\"p\">(</span> <span class=\"p\">)</span> <span class=\"k\">const</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">char</span> <span class=\"o\">*</span> <span class=\"n\">pptr</span> <span class=\"p\">(</span> <span class=\"p\">)</span> <span class=\"k\">const</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h5 id=\"13设置-get-和-put-指针可达区域的上下界\">1.3、设置 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针可达区域的上下界</h5>\n\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">setg</span> <span class=\"p\">(</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gbeg</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gnext</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gend</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\n</ul>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">setp</span> <span class=\"p\">(</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pbeg</span><span class=\"p\">,</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pend</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\n <li><code class=\"language-plaintext highlighter-rouge\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\n</ul>\n\n<h4 id=\"2memorystreambuf\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code></h4>\n\n<p>类定义:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryStreamBuf</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">streambuf</span> <span class=\"p\">{</span>\n <span class=\"k\">friend</span> <span class=\"k\">class</span> <span class=\"nc\">ScopedMemoryClip</span><span class=\"p\">;</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">();</span>\n \n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">written</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">void</span> <span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gCurrent</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pCurrent</span><span class=\"p\">();</span> <span class=\"c1\">// Explaint below</span>\n \n<span class=\"nl\">private:</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">overflow</span><span class=\"p\">(</span><span class=\"n\">int_type</span> <span class=\"n\">c</span><span class=\"p\">);</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">underflow</span><span class=\"p\">();</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">int</span> <span class=\"n\">sync</span><span class=\"p\">();</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_written</span><span class=\"p\">;</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"n\">MemoryStreamBuf</span><span class=\"p\">();</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"k\">operator</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span><span class=\"p\">);</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 是 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\n\n<h5 id=\"21移动内置的-get-和-put-指针\">2.1、移动内置的 <code class=\"language-plaintext highlighter-rouge\">get</code> 和 <code class=\"language-plaintext highlighter-rouge\">put</code> 指针:</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针都移动:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">gbump</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22获取-get-和-put-指针当前位置\">2.2、获取 get 和 put 指针当前位置:</h5>\n\n<p>封装 <code class=\"language-plaintext highlighter-rouge\">streambuf</code> 的 <code class=\"language-plaintext highlighter-rouge\">gptr</code> 和 <code class=\"language-plaintext highlighter-rouge\">pptr</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">gCurrent</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">gptr</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n<span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">pptr</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23获取缓冲区的起始位置和大小\">2.3、获取缓冲区的起始位置和大小:</h5>\n\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n \n<span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"24缓冲区的已写字节数\">2.4、缓冲区的已写字节数</h5>\n\n<p>读取(其中也可能发生设置操作):</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"kt\">int</span> <span class=\"n\">written</span> <span class=\"o\">=</span> <span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"c1\">// 已写字节数</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">written</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">written</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">written</span> <span class=\"o\">></span> <span class=\"n\">_written</span><span class=\"p\">)</span> <span class=\"c1\">// 保存已写字节数</span>\n <span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"p\">)</span><span class=\"n\">written</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">_written</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设置:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_written</span><span class=\"o\">=</span><span class=\"n\">size</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"25显式设定-put-和-get-指针位置\">2.5、显式设定 <code class=\"language-plaintext highlighter-rouge\">put</code> 和 <code class=\"language-plaintext highlighter-rouge\">get</code> 指针位置</h5>\n\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">pos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"c1\">// 保存已写字节数</span>\n <span class=\"n\">written</span><span class=\"p\">();</span> <span class=\"c1\">// Save nb char written</span>\n \n <span class=\"c1\">// 移动 put 指针</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n <span class=\"n\">pbump</span><span class=\"p\">((</span><span class=\"kt\">int</span><span class=\"p\">)</span> <span class=\"n\">pos</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 移动 get 指针</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">pos</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"26-修改缓冲区大小\">2.6 修改缓冲区大小</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"c1\">// 大小标识</span>\n <span class=\"n\">_bufferSize</span> <span class=\"o\">=</span> <span class=\"n\">newSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// gptr 当前位置</span>\n <span class=\"kt\">int</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">gCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 gptr 可达范围和当前位置</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">pos</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span> \n <span class=\"c1\">// pptr 当前位置</span>\n <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pos</span> <span class=\"o\">></span> <span class=\"n\">_bufferSize</span><span class=\"p\">)</span> <span class=\"n\">pos</span> <span class=\"o\">=</span> <span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 pptr 可达范围和当前位置</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"27构造函数拷贝构造函数和析构函数\">2.7、构造函数、拷贝构造函数和析构函数</h5>\n\n<p>构造函数会设定 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 和 <code class=\"language-plaintext highlighter-rouge\">gptr</code>,并初始化 <code class=\"language-plaintext highlighter-rouge\">pBuffer</code> 和 <code class=\"language-plaintext highlighter-rouge\">bufferSize</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> <span class=\"n\">_pBuffer</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">),</span><span class=\"n\">_bufferSize</span><span class=\"p\">(</span><span class=\"n\">bufferSize</span><span class=\"p\">),</span><span class=\"n\">_written</span><span class=\"p\">(</span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span><span class=\"p\">,</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构函数会拷贝对方的 <code class=\"language-plaintext highlighter-rouge\">pBuffer</code>、<code class=\"language-plaintext highlighter-rouge\">bufferSizse</code>、<code class=\"language-plaintext highlighter-rouge\">_written</code>,并设定 <code class=\"language-plaintext highlighter-rouge\">gptr</code>、<code class=\"language-plaintext highlighter-rouge\">pptr</code>。注意设定 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 时,要分别调用 <code class=\"language-plaintext highlighter-rouge\">setp</code> 和 <code class=\"language-plaintext highlighter-rouge\">pbump</code>,因为 <code class=\"language-plaintext highlighter-rouge\">setp</code> 仅将 <code class=\"language-plaintext highlighter-rouge\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span> <span class=\"n\">_pBuffer</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">),</span><span class=\"n\">_bufferSize</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">),</span><span class=\"n\">_written</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_written</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">gCurrent</span><span class=\"p\">(),</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">pbump</span><span class=\"p\">((</span><span class=\"kt\">int</span><span class=\"p\">)(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span><span class=\"o\">-</span><span class=\"n\">_pBuffer</span><span class=\"p\">));</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>析构函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">::~</span><span class=\"n\">MemoryStreamBuf</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"二io-流\">二、IO 流</h3>\n\n<h4 id=\"1了解-stdios\">1、了解 <code class=\"language-plaintext highlighter-rouge\">std::ios</code></h4>\n\n<p>Initialize object [<code class=\"language-plaintext highlighter-rouge\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"nf\">init</span> <span class=\"p\">(</span> <span class=\"n\">streambuf</span><span class=\"o\">*</span> <span class=\"n\">sb</span> <span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>初始化后如下函数的返回值:</p>\n\n<table>\n <thead>\n <tr>\n <th>member function</th>\n <th>value</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>rdbuf()</td>\n <td>sb</td>\n </tr>\n <tr>\n <td>tie()</td>\n <td>0</td>\n </tr>\n <tr>\n <td>rdstate()</td>\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\n </tr>\n <tr>\n <td>exceptions()</td>\n <td>goodbit</td>\n </tr>\n <tr>\n <td>flags()</td>\n <td>skipws | dec</td>\n </tr>\n <tr>\n <td>width()</td>\n <td>0</td>\n </tr>\n <tr>\n <td>precision()</td>\n <td>6</td>\n </tr>\n <tr>\n <td>fill()</td>\n <td>‘ ’ (whitespace)</td>\n </tr>\n <tr>\n <td>getloc()</td>\n <td>a copy of locale()</td>\n </tr>\n </tbody>\n</table>\n\n<h4 id=\"2memoryios\">2、<code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code></h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code>,且是 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 和 <code class=\"language-plaintext highlighter-rouge\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\"language-plaintext highlighter-rouge\">std::ios</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryIOS</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"k\">virtual</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ios</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">MemoryIOS</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryIOS</span><span class=\"p\">();</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">*</span> <span class=\"n\">rdbuf</span><span class=\"p\">();</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"kt\">void</span> <span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"kt\">void</span> <span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">);</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">available</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"n\">MemoryStreamBuf</span> <span class=\"n\">_buf</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"21构造函数拷贝构造函数和析构函数\">2.1、构造函数、拷贝构造函数和析构函数</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span><span class=\"n\">_buf</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">poco_ios_init</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">poco_ios_init</code> 为 <code class=\"language-plaintext highlighter-rouge\">init</code> 的宏定义,用于初始化成员 <code class=\"language-plaintext highlighter-rouge\">_buf</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">MemoryIOS</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span><span class=\"n\">_buf</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">.</span><span class=\"n\">_buf</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">poco_ios_init</span><span class=\"p\">(</span><span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryIOS</span><span class=\"o\">::~</span><span class=\"n\">MemoryIOS</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"22得到-memorystreambuf-成员的地址\">2.2、得到 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的地址</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">*</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">rdbuf</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">&</span><span class=\"n\">_buf</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"23当前位置\">2.3、当前位置</h5>\n\n<p>这是一个纯虚函数,由 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 和 <code class=\"language-plaintext highlighter-rouge\">MemoryOutpuStream</code> 继承时实现:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">virtual</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">()</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<h5 id=\"24封装-memorystreambuf-成员的一些函数\">2.4、封装 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的一些函数</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">begin</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">begin</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">begin</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">resize</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">newSize</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">resize</span><span class=\"p\">(</span><span class=\"n\">newSize</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">next</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">next</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">position</code> 封装为 <code class=\"language-plaintext highlighter-rouge\">reset</code></p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code> <span class=\"kt\">void</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">newPos</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"o\">>=</span><span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">position</span><span class=\"p\">(</span><span class=\"n\">newPos</span><span class=\"p\">);</span>\n <span class=\"n\">clear</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"25-缓冲区可读数据的字节数\">2.5 缓冲区可读数据的字节数</h5>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">UInt32</span> <span class=\"n\">MemoryIOS</span><span class=\"o\">::</span><span class=\"n\">available</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"kt\">int</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">size</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"p\">(</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">begin</span><span class=\"p\">());</span> <span class=\"c1\">// 缓冲区剩余可读数据字节数</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">result</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">UInt32</span><span class=\"p\">)</span><span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3输入流\">3、输入流</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryInputStream</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">MemoryIOS</span><span class=\"p\">,</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">istream</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"c1\">/// Creates a MemoryInputStream for the given memory area,</span>\n <span class=\"c1\">/// ready for reading.</span>\n <span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryInputStream</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">();</span>\n <span class=\"c1\">/// Destroys the MemoryInputStream.</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 以及 <code class=\"language-plaintext highlighter-rouge\">istream</code>。<code class=\"language-plaintext highlighter-rouge\">istream</code> 是 <code class=\"language-plaintext highlighter-rouge\">iostream</code> 中的 <code class=\"language-plaintext highlighter-rouge\">basic_istream</code> 别名(<code class=\"language-plaintext highlighter-rouge\">typedef</code>)。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> \n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"k\">const_cast</span><span class=\"o\"><</span><span class=\"kt\">char</span><span class=\"o\">*></span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">),</span> <span class=\"n\">bufferSize</span><span class=\"p\">),</span> <span class=\"n\">istream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryInputStream</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">),</span> <span class=\"n\">istream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryInputStream</span><span class=\"o\">::~</span><span class=\"n\">MemoryInputStream</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>唯一的一个成员函数是 <code class=\"language-plaintext highlighter-rouge\">current</code>,封装了 <code class=\"language-plaintext highlighter-rouge\">MemoryIOS</code> 的 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员的 <code class=\"language-plaintext highlighter-rouge\">gCurrent</code> 函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryInputStream</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">gCurrent</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"4输出流\">4、输出流</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">MemoryOutputStream</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">MemoryIOS</span><span class=\"p\">,</span> <span class=\"k\">public</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">ostream</span>\n <span class=\"c1\">/// An input stream for reading from a memory area.</span>\n<span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">);</span>\n <span class=\"c1\">/// Creates a MemoryOutputStream for the given memory area,</span>\n <span class=\"c1\">/// ready for writing.</span>\n <span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryOutputStream</span><span class=\"o\">&</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">();</span>\n <span class=\"c1\">/// Destroys the MemoryInputStream.</span>\n \n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">written</span><span class=\"p\">();</span>\n <span class=\"kt\">void</span> <span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">);</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">current</span><span class=\"p\">();</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h5 id=\"41-构造函数拷贝构造函数和析构函数\">4.1 构造函数、拷贝构造函数和析构函数</h5>\n\n<p>如下,不赘述了。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">bufferSize</span><span class=\"p\">)</span><span class=\"o\">:</span> \n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">pBuffer</span><span class=\"p\">,</span> <span class=\"n\">bufferSize</span><span class=\"p\">),</span> <span class=\"n\">ostream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n<span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(</span><span class=\"n\">MemoryOutputStream</span><span class=\"o\">&</span> <span class=\"n\">other</span><span class=\"p\">)</span><span class=\"o\">:</span>\n <span class=\"n\">MemoryIOS</span><span class=\"p\">(</span><span class=\"n\">other</span><span class=\"p\">),</span> <span class=\"n\">ostream</span><span class=\"p\">(</span><span class=\"n\">rdbuf</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n \n<span class=\"n\">MemoryOutputStream</span><span class=\"o\">::~</span><span class=\"n\">MemoryOutputStream</span><span class=\"p\">(){</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"42-读取和设定已写字节数\">4.2 读取和设定已写字节数</h5>\n\n<p>读取:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">written</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>设定:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">void</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">size</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">written</span><span class=\"p\">(</span><span class=\"n\">size</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h5 id=\"43-当前位置\">4.3 当前位置</h5>\n\n<p>与 <code class=\"language-plaintext highlighter-rouge\">MemoryInputStream</code> 中的封装类似:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">MemoryOutputStream</span><span class=\"o\">::</span><span class=\"n\">current</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"n\">rdbuf</span><span class=\"p\">()</span><span class=\"o\">-></span><span class=\"n\">pCurrent</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"三局部内存片\">三、局部内存片</h3>\n\n<p>在第一部分的流缓冲区介绍 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 中有一个 <code class=\"language-plaintext highlighter-rouge\">MemoryStreamBuf</code> 成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">ScopedMemoryClip</span> <span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">ScopedMemoryClip</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">buffer</span><span class=\"p\">,</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">offset</span><span class=\"p\">);</span>\n <span class=\"o\">~</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">();</span>\n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Int32</span> <span class=\"n\">offset</span><span class=\"p\">);</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">_offset</span><span class=\"p\">;</span>\n <span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">_buffer</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<h4 id=\"1构造函数\">1、构造函数</h4>\n\n<p>构造函数传入的参数对应的就是 <code class=\"language-plaintext highlighter-rouge\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\"language-plaintext highlighter-rouge\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">(</span><span class=\"n\">MemoryStreamBuf</span><span class=\"o\">&</span> <span class=\"n\">buffer</span><span class=\"p\">,</span> <span class=\"n\">UInt32</span> <span class=\"n\">offset</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_offset</span><span class=\"p\">(</span><span class=\"n\">offset</span><span class=\"p\">),</span> <span class=\"n\">_buffer</span><span class=\"p\">(</span><span class=\"n\">buffer</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_offset</span> <span class=\"o\">>=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">_offset</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span> <span class=\"o\">-</span> <span class=\"mi\">1</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_offset</span> <span class=\"o\"><</span> <span class=\"mi\">0</span><span class=\"p\">)</span>\n <span class=\"n\">_offset</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">_offset</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2析构函数\">2、析构函数</h4>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::~</span><span class=\"n\">ScopedMemoryClip</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"o\">-</span><span class=\"p\">(</span><span class=\"n\">Int32</span><span class=\"p\">)</span><span class=\"n\">_offset</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"3缓冲区切割\">3、缓冲区切割</h4>\n\n<p>可以看到构造函数和析构函数中都调用了 <code class=\"language-plaintext highlighter-rouge\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\n\n<ul>\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\n</ul>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">ScopedMemoryClip</span><span class=\"o\">::</span><span class=\"n\">clip</span><span class=\"p\">(</span><span class=\"n\">Int32</span> <span class=\"n\">offset</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n \n <span class=\"c1\">// 获取到 gptr</span>\n <span class=\"kt\">char</span><span class=\"o\">*</span> <span class=\"n\">gpos</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">gCurrent</span><span class=\"p\">();</span>\n \n <span class=\"c1\">// 偏移缓冲区地址,并修改缓冲区大小</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span> <span class=\"o\">-=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\n <span class=\"kt\">int</span> <span class=\"n\">ppos</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">pCurrent</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 设置 gptr 可达区域和位置</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">setg</span><span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">gpos</span><span class=\"p\">,</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 设置 pptr 可达区域和位置</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">setp</span><span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span><span class=\"p\">,</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_pBuffer</span> <span class=\"o\">+</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">);</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">pbump</span><span class=\"p\">(</span><span class=\"n\">ppos</span><span class=\"p\">);</span>\n \n <span class=\"c1\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\"><</span> <span class=\"n\">offset</span><span class=\"p\">)</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\n <span class=\"k\">else</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">-=</span> <span class=\"n\">offset</span><span class=\"p\">;</span>\n \n <span class=\"c1\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">></span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">)</span>\n <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_written</span> <span class=\"o\">=</span> <span class=\"n\">_buffer</span><span class=\"p\">.</span><span class=\"n\">_bufferSize</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>一名出色软件工程师的技术基本功:编程与工具</title>\n \t<meta name=\"description\" content=\"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>一名出色软件工程师的技术基本功:编程与工具</h2>\t\t\n\t<time datetime=\"2012-05-15T17:06:59+00:00\" class=\"by-line\">15 May 2012, 广州 | 作者 麦克船长 | 总计 2132 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"0写在前面\">0、写在前面</h3>\n\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\n\n<h3 id=\"1编程\">1、编程</h3>\n\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\n\n<h4 id=\"如果你精通c语言\">如果你精通 C 语言</h4>\n\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\n\n<h4 id=\"如果你精通c语言-1\">如果你精通 C++ 语言</h4>\n\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\n\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\n\n<h4 id=\"精通的关键还是针对语言核心来说的\">精通的关键,还是针对语言核心来说的。</h4>\n\n<p>第一,你要对这个语言的语法特性熟稔;</p>\n\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\n\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\n\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\n\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\n\n<h4 id=\"与计算机网络的基础结构相关联的技术实现\">与计算机、网络的基础结构相关联的技术实现</h4>\n\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\n\n<h3 id=\"2工具\">2、工具</h3>\n\n<p>对于工具有三个层面:</p>\n\n<p>第一,是熟练的使用一些工具。</p>\n\n<p>第二,是能够发现提高生产力的工具。</p>\n\n<p>第三,是能够在无可用工具时自己编写工具。</p>\n\n<p>那么都有哪些最最最基本的工具呢?</p>\n\n<h4 id=\"ideintegrateddevelopmentenvironment\">IDE(Integrated Development Environment)</h4>\n\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\n\n<h4 id=\"vcsversioncontrolsystem\">VCS(Version Control System)</h4>\n\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\n\n<blockquote>\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black & white.</p>\n</blockquote>\n\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\n\n<h4 id=\"clicommandlineinterface\">CLI(Command Line Interface)</h4>\n\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\n\n<h3 id=\"3结语\">3、结语</h3>\n\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</h2>\t\t\n\t<time datetime=\"2012-06-07T15:34:18+00:00\" class=\"by-line\">07 Jun 2012, 广州 | 作者 麦克船长 | 总计 1538 字</time>\n\t<div class=\"content\">\n\t\t<p>OpenRTMFP/Cumulus 提供了 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code>。</p>\n\n<p>一般来说,Thread A 会准备好要 <code class=\"language-plaintext highlighter-rouge\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\"language-plaintext highlighter-rouge\">push</code> 消息。</p>\n\n<p>但是 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\"language-plaintext highlighter-rouge\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\" alt=\"image\" /></p>\n\n<p>由于在 <code class=\"language-plaintext highlighter-rouge\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\"language-plaintext highlighter-rouge\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\"language-plaintext highlighter-rouge\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\" alt=\"image\" /></p>\n\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">createAMFMessage</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">std</span><span class=\"o\">::</span><span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">)</span>\n \n <span class=\"c1\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"p\">(</span><span class=\"n\">_closed</span> <span class=\"o\">||</span> <span class=\"n\">signature</span><span class=\"p\">.</span><span class=\"n\">empty</span><span class=\"p\">()</span> <span class=\"o\">||</span> <span class=\"n\">_band</span><span class=\"p\">.</span><span class=\"n\">failed</span><span class=\"p\">()))</span> <span class=\"p\">{</span>\n <span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span> <span class=\"o\">=</span> <span class=\"k\">new</span> <span class=\"n\">MessageBuffered</span><span class=\"p\">();</span>\n <span class=\"n\">MessageBuffered</span><span class=\"o\">&</span> <span class=\"n\">message</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"n\">writeResponseHeader</span><span class=\"p\">(</span><span class=\"n\">message</span><span class=\"p\">.</span><span class=\"n\">rawWriter</span><span class=\"p\">,</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"n\">pMessage</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"n\">MessageBuffered</span><span class=\"o\">&</span> <span class=\"n\">message</span><span class=\"p\">(</span><span class=\"n\">_MessageNull</span><span class=\"p\">);</span>\n <span class=\"n\">writeResponseHeader</span><span class=\"p\">(</span><span class=\"n\">message</span><span class=\"p\">.</span><span class=\"n\">rawWriter</span><span class=\"p\">,</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n <span class=\"k\">return</span> <span class=\"nb\">NULL</span><span class=\"p\">;</span>\n<span class=\"err\">}</span>\n</code></pre></div></div>\n\n<p>然后再调用时最后再增加 <code class=\"language-plaintext highlighter-rouge\">push</code> 操作:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"kt\">void</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">pushAMFMessage</span><span class=\"p\">(</span><span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMessage</span> <span class=\"o\">!=</span> <span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">_messages</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\n\n<p><img src=\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\" alt=\"image\" /></p>\n\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"cm\">/*\n * author: michael\n * date: June 6th, 2012\n * type: add\n */</span>\n<span class=\"kt\">void</span> <span class=\"n\">FlowWriter</span><span class=\"o\">::</span><span class=\"n\">pushAMFMessage</span><span class=\"p\">(</span><span class=\"n\">MessageBuffered</span><span class=\"o\">*</span> <span class=\"n\">pMessage</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">pMessage</span> <span class=\"o\">!=</span> <span class=\"nb\">NULL</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Mutex</span><span class=\"o\">::</span><span class=\"n\">ScopedLock</span> <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">msgQueueMutex</span><span class=\"p\">);</span>\n <span class=\"n\">_messages</span><span class=\"p\">.</span><span class=\"n\">push_back</span><span class=\"p\">(</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样就基本解决了这个线程安全问题。</p>\n\n<p>另外,使用 <code class=\"language-plaintext highlighter-rouge\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</h2>\t\t\n\t<time datetime=\"2012-06-25T02:56:26+00:00\" class=\"by-line\">25 Jun 2012, 广州 | 作者 麦克船长 | 总计 2111 字</time>\n\t<div class=\"content\">\n\t\t<p><code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中的线程都是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code>,在其中封装 <code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\"language-plaintext highlighter-rouge\">Startable</code> 中的 <code class=\"language-plaintext highlighter-rouge\">start</code> 函数如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">)</span> <span class=\"c1\">// if running</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n \n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span>\n <span class=\"s\">\"Try to start up a new thread inherited from Startable\"</span><span class=\"p\">);</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">_process</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> \n <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span>\n <span class=\"s\">\"Impossible to start the thread : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这样一个类继承 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\"language-plaintext highlighter-rouge\">Startable::start()</code>,然后调用到该类自己的 <code class=\"language-plaintext highlighter-rouge\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\"language-plaintext highlighter-rouge\">SocketManager</code> 为例:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">SocketManager</span><span class=\"o\">::</span><span class=\"n\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"err\">…</span> \n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">running</span><span class=\"p\">())</span> <span class=\"p\">{</span>\n <span class=\"err\">…</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>我们要看看这个 <code class=\"language-plaintext highlighter-rouge\">running()</code> 是怎么回事,如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kr\">inline</span> <span class=\"kt\">bool</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">running</span><span class=\"p\">()</span> <span class=\"k\">const</span> <span class=\"p\">{</span>\n <span class=\"k\">return</span> <span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>很简单,就是通过 <code class=\"language-plaintext highlighter-rouge\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 是什么时候被设置为 <code class=\"language-plaintext highlighter-rouge\">false</code> 的呢?就是上面的 <code class=\"language-plaintext highlighter-rouge\">start()</code>,这里存在的一个问题就是先 <code class=\"language-plaintext highlighter-rouge\">start</code> 线程,再设置 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">false</code>。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_thread.start(_process);\n_stop=false;\n</code></pre></div></div>\n\n<p>而 <code class=\"language-plaintext highlighter-rouge\">start()</code> 之后 <code class=\"language-plaintext highlighter-rouge\">run()</code> 的时候就开始通过 <code class=\"language-plaintext highlighter-rouge\">running()</code> 来判断 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 值了。所以你会在使用 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\" alt=\"image\" /></p>\n\n<p>它们是:</p>\n\n<ul>\n <li>主线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 线程</li>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 线程</li>\n</ul>\n\n<p>而异常情况可能是 <code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 没有启动,甚至 <code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 都没有启动。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\" alt=\"image\" /></p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">MainSockets</code> 和 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 都没有启动的情况 T.T</p>\n\n<p><img src=\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\" alt=\"image\" /></p>\n\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\n\n<p>解决办法就是将 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\"language-plaintext highlighter-rouge\">_stop</code> 值为 <code class=\"language-plaintext highlighter-rouge\">true</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">start</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_stop</span><span class=\"p\">)</span> <span class=\"c1\">// if running</span>\n <span class=\"k\">return</span><span class=\"p\">;</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutex</span><span class=\"p\">);</span>\n \n <span class=\"k\">if</span><span class=\"p\">(</span><span class=\"n\">_haveToJoin</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"n\">_haveToJoin</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n \n <span class=\"k\">try</span> <span class=\"p\">{</span>\n <span class=\"n\">DEBUG</span><span class=\"p\">(</span>\n <span class=\"s\">\"Try to start up a new thread inherited from Startable\"</span><span class=\"p\">);</span>\n <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span><span class=\"o\">=</span>\n <span class=\"nb\">false</span><span class=\"p\">;</span>\n <span class=\"p\">}</span>\n <span class=\"kr\">_thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">_process</span><span class=\"p\">);</span>\n <span class=\"n\">_haveToJoin</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> \n <span class=\"k\">catch</span> <span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Exception</span><span class=\"o\">&</span> <span class=\"n\">ex</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"p\">{</span>\n <span class=\"n\">ScopedLock</span>\n \n <span class=\"n\">lock</span><span class=\"p\">(</span><span class=\"n\">_mutexStop</span><span class=\"p\">);</span>\n <span class=\"n\">_stop</span> <span class=\"o\">=</span> \n <span class=\"nb\">true</span><span class=\"p\">;</span> \n <span class=\"c1\">// June 25th, 2012, Michael@YY</span>\n <span class=\"p\">}</span>\n <span class=\"n\">ERROR</span><span class=\"p\">(</span>\n <span class=\"s\">\"Impossible to start the thread : %s\"</span><span class=\"p\">,</span><span class=\"n\">ex</span><span class=\"p\">.</span><span class=\"n\">displayText</span><span class=\"p\">().</span><span class=\"n\">c_str</span><span class=\"p\">());</span>\n <span class=\"p\">}</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</title>\n \t<meta name=\"description\" content=\"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</h2>\t\t\n\t<time datetime=\"2012-07-23T03:07:43+00:00\" class=\"by-line\">23 Jul 2012, 广州 | 作者 麦克船长 | 总计 3111 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#1客户端发布publishing-on-client-side\" id=\"markdown-toc-1客户端发布publishing-on-client-side\">1、客户端发布(Publishing on client side)</a></li>\n <li><a href=\"#2服务器端server-side\" id=\"markdown-toc-2服务器端server-side\">2、服务器端(Server-side)</a></li>\n <li><a href=\"#3客户端订阅subscribing-on-client-side\" id=\"markdown-toc-3客户端订阅subscribing-on-client-side\">3、客户端订阅(Subscribing on client side)</a></li>\n <li><a href=\"#4reference\" id=\"markdown-toc-4reference\">4、Reference</a></li>\n</ul>\n\n<p>整个流程概括如下:</p>\n\n<p>Flash 客户端通过 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 与 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 建立连接,然后通过 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\n\n<h3 id=\"1客户端发布publishing-on-client-side\">1、客户端发布(Publishing on client side)</h3>\n\n<p>通过 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\"/2012/04/10/openrtmfp-cumulus-1/\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\"language-plaintext highlighter-rouge\">nc</code> 是一个 <code class=\"language-plaintext highlighter-rouge\">NetConnection</code> 对象。</p>\n\n<div class=\"language-actionscript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">nc</span><span class=\"p\">.</span><span class=\"nx\">connect</span><span class=\"p\">(</span><span class=\"s2\">\"rtmfp://localhost:1935\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\"language-plaintext highlighter-rouge\">ns1</code> 是一个 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 对象。</p>\n\n<div class=\"language-actionscript highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nx\">ns1</span><span class=\"p\">.</span><span class=\"nx\">publish</span><span class=\"p\">(</span><span class=\"s2\">\"poechant_media_flow\"</span><span class=\"p\">,</span> <span class=\"s2\">\"live\"</span><span class=\"p\">)</span><span class=\"o\">;</span>\n</code></pre></div></div>\n\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\n\n<h3 id=\"2服务器端server-side\">2、服务器端(Server-side)</h3>\n\n<p>Cumulus 通过 <code class=\"language-plaintext highlighter-rouge\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">receive</span><span class=\"p\">(</span><span class=\"n\">RTMFPReceiving</span><span class=\"o\">&</span> <span class=\"n\">rtmfpReceiving</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\n\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\"language-plaintext highlighter-rouge\">Handshake</code> 类的 <code class=\"language-plaintext highlighter-rouge\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">ServerSession</span><span class=\"o\">::</span><span class=\"n\">packetHandler</span><span class=\"p\">(</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">packet</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">Flow</span><span class=\"o\">::</span><span class=\"n\">fragmentSortedHandler</span><span class=\"p\">(</span><span class=\"n\">UInt64</span> <span class=\"n\">stage</span><span class=\"p\">,</span><span class=\"n\">PacketReader</span><span class=\"o\">&</span> <span class=\"n\">fragment</span><span class=\"p\">,</span><span class=\"n\">UInt8</span> <span class=\"n\">flags</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">switch</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AMF_WITH_HANDLER</span><span class=\"p\">:</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AMF</span><span class=\"p\">:</span>\n <span class=\"n\">messageHandler</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">amf</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">AUDIO</span><span class=\"p\">:</span>\n <span class=\"n\">audioHandler</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"k\">case</span> <span class=\"n\">Message</span><span class=\"o\">::</span><span class=\"n\">VIDEO</span><span class=\"p\">:</span>\n <span class=\"n\">videoHandler</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n <span class=\"k\">break</span><span class=\"p\">;</span>\n <span class=\"nl\">default:</span>\n <span class=\"n\">rawHandler</span><span class=\"p\">(</span><span class=\"n\">type</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"n\">pMessage</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>接下来在 <code class=\"language-plaintext highlighter-rouge\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">for</span> <span class=\"p\">(</span><span class=\"n\">it</span> <span class=\"o\">=</span> <span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span> <span class=\"n\">it</span> <span class=\"o\">!=</span> <span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span> <span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushAudioPacket</span><span class=\"p\">(</span><span class=\"n\">time</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n \n<span class=\"k\">for</span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span><span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span><span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushVideoPacket</span><span class=\"p\">(</span><span class=\"n\">time</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n \n<span class=\"k\">for</span><span class=\"p\">(</span><span class=\"n\">it</span><span class=\"o\">=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">begin</span><span class=\"p\">();</span><span class=\"n\">it</span><span class=\"o\">!=</span><span class=\"n\">_listeners</span><span class=\"p\">.</span><span class=\"n\">end</span><span class=\"p\">();</span><span class=\"o\">++</span><span class=\"n\">it</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">it</span><span class=\"o\">-></span><span class=\"n\">second</span><span class=\"o\">-></span><span class=\"n\">pushDataPacket</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span><span class=\"n\">packet</span><span class=\"p\">);</span>\n <span class=\"n\">packet</span><span class=\"p\">.</span><span class=\"n\">reset</span><span class=\"p\">(</span><span class=\"n\">pos</span><span class=\"p\">);</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中的 <code class=\"language-plaintext highlighter-rouge\">_listeners</code> 就是该 <code class=\"language-plaintext highlighter-rouge\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Listener</span><span class=\"o\">&</span> <span class=\"n\">addListener</span><span class=\"p\">(</span>\n <span class=\"n\">Peer</span><span class=\"o\">&</span> <span class=\"n\">peer</span><span class=\"p\">,</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">id</span><span class=\"p\">,</span>\n <span class=\"n\">FlowWriter</span><span class=\"o\">&</span> <span class=\"n\">writer</span><span class=\"p\">,</span>\n <span class=\"kt\">bool</span> <span class=\"n\">unbuffered</span><span class=\"p\">);</span>\n \n<span class=\"kt\">void</span> <span class=\"nf\">removeListener</span><span class=\"p\">(</span>\n <span class=\"n\">Peer</span><span class=\"o\">&</span> <span class=\"n\">peer</span><span class=\"p\">,</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">id</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>这两个函数来实现的。</p>\n\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\n\n<h3 id=\"3客户端订阅subscribing-on-client-side\">3、客户端订阅(Subscribing on client side)</h3>\n\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>ns2.play(\"poechant_media_flow\");\n</code></pre></div></div>\n\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\"language-plaintext highlighter-rouge\">NetStream::send(…)</code> 的,用于发送 <code class=\"language-plaintext highlighter-rouge\">Data</code>,<code class=\"language-plaintext highlighter-rouge\">Audio</code> 和 <code class=\"language-plaintext highlighter-rouge\">Video</code> 的程序可以参考该例修改。</p>\n\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\"language-plaintext highlighter-rouge\">NetStream</code> 初始化的时候,传入 <code class=\"language-plaintext highlighter-rouge\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\"language-plaintext highlighter-rouge\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\n\n<h3 id=\"4reference\">4、Reference</h3>\n\n<ul>\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\n</ul>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</title>\n \t<meta name=\"description\" content=\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</h2>\t\t\n\t<time datetime=\"2012-08-04T17:58:17+00:00\" class=\"by-line\">04 Aug 2012, 广州 | 作者 麦克船长 | 总计 5236 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一rtmfpserver-线程的启动和等待\" id=\"markdown-toc-一rtmfpserver-线程的启动和等待\">一、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的启动和等待</a> <ul>\n <li><a href=\"#1pocothread\" id=\"markdown-toc-1pocothread\">1、<code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code></a></li>\n <li><a href=\"#2封装一个可运行线程的类\" id=\"markdown-toc-2封装一个可运行线程的类\">2、封装一个可运行线程的类</a></li>\n <li><a href=\"#3启动-rtmfpserver-线程\" id=\"markdown-toc-3启动-rtmfpserver-线程\">3、启动 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</a></li>\n <li><a href=\"#4rtmfpserver-线程等待\" id=\"markdown-toc-4rtmfpserver-线程等待\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程等待</a></li>\n </ul>\n </li>\n <li><a href=\"#二rtmfpmanager-对-rtmfpserver-的影响\" id=\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\">二、<code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的影响</a></li>\n</ul>\n\n<h3 id=\"一rtmfpserver-线程的启动和等待\">一、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的启动和等待</h3>\n\n<h4 id=\"1pocothread\">1、<code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code></h4>\n\n<p>Cumulus 大量使用了 <code class=\"language-plaintext highlighter-rouge\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">PoechantRunnable</span><span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Runnable</span> <span class=\"p\">{</span>\n <span class=\"k\">virtual</span> <span class=\"kt\">void</span> <span class=\"n\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"c1\">// your codes</span>\n <span class=\"p\">}</span>\n<span class=\"p\">};</span>\n \n<span class=\"kt\">int</span> <span class=\"nf\">main</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">PoechantRunnable</span> <span class=\"n\">runnable</span><span class=\"p\">;</span> <span class=\"c1\">// Image that it's a gift</span>\n <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Thread</span> <span class=\"kr\">thread</span><span class=\"p\">;</span> <span class=\"c1\">// And… thread is just like your girl</span>\n <span class=\"kr\">thread</span><span class=\"p\">.</span><span class=\"n\">start</span><span class=\"p\">(</span><span class=\"n\">runnable</span><span class=\"p\">);</span> <span class=\"c1\">// Okay, give your sweet babe the gift :)</span>\n <span class=\"kr\">thread</span><span class=\"p\">.</span><span class=\"n\">join</span><span class=\"p\">();</span>\n <span class=\"k\">return</span> <span class=\"mi\">0</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<h4 id=\"2封装一个可运行线程的类\">2、封装一个可运行线程的类</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中实现了一个 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 类,该类继承了 <code class=\"language-plaintext highlighter-rouge\">Runnable</code>,就是上面那个 <code class=\"language-plaintext highlighter-rouge\">gift</code> 喽。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">StartableProcess</span> <span class=\"o\">:</span> <span class=\"k\">public</span> <span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Runnable</span><span class=\"p\">{</span>\n<span class=\"nl\">public:</span>\n <span class=\"n\">StartableProcess</span><span class=\"p\">(</span><span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">startable</span><span class=\"p\">);</span>\n<span class=\"nl\">private:</span>\n <span class=\"kt\">void</span> <span class=\"n\">run</span><span class=\"p\">();</span>\n <span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">_startable</span><span class=\"p\">;</span>\n<span class=\"p\">};</span>\n</code></pre></div></div>\n\n<p>可以看到其中有 <code class=\"language-plaintext highlighter-rouge\">Startable& _startable</code> 引用成员,它并没有继承 <code class=\"language-plaintext highlighter-rouge\">Runnable</code>,而是封装了 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 和 <code class=\"language-plaintext highlighter-rouge\">Poco::Thread</code>:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">Thread</span> <span class=\"kr\">_thread</span><span class=\"p\">;</span>\n<span class=\"n\">StartableProcess</span> <span class=\"n\">_process</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>这里 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 封装了一个 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 成员,与 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\n\n<h4 id=\"3启动-rtmfpserver-线程\">3、启动 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程</h4>\n<p>我们可以看到在 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的构造函数中初始化了 <code class=\"language-plaintext highlighter-rouge\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\"language-plaintext highlighter-rouge\">(Flag Field)_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">true</code>,因为它还没有调用启动函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"k\">const</span> <span class=\"n\">string</span><span class=\"o\">&</span> <span class=\"n\">name</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_name</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">),</span>\n <span class=\"kr\">_thread</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">),</span>\n <span class=\"n\">_stop</span><span class=\"p\">(</span><span class=\"nb\">true</span><span class=\"p\">),</span>\n <span class=\"n\">_haveToJoin</span><span class=\"p\">(</span><span class=\"nb\">false</span><span class=\"p\">),</span>\n <span class=\"n\">_process</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>初始化 <code class=\"language-plaintext highlighter-rouge\">_process</code> 时,调用 <code class=\"language-plaintext highlighter-rouge\">StartableProcess</code> 构造函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">StartableProcess</span><span class=\"o\">::</span><span class=\"n\">StartableProcess</span><span class=\"p\">(</span><span class=\"n\">Startable</span><span class=\"o\">&</span> <span class=\"n\">startable</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_startable</span><span class=\"p\">(</span><span class=\"n\">startable</span><span class=\"p\">){</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>传入 <code class=\"language-plaintext highlighter-rouge\">_startable</code> 的引用。在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的,然后通过调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 来启动,启动后会响应到 <code class=\"language-plaintext highlighter-rouge\">run()</code>。下面我们以 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程为例。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 类是继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 类的:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">RTMFPServer</span>\n <span class=\"o\">:</span> <span class=\"k\">private</span> <span class=\"n\">Gateway</span><span class=\"p\">,</span>\n <span class=\"k\">protected</span> <span class=\"n\">Handler</span><span class=\"p\">,</span>\n <span class=\"k\">private</span> <span class=\"n\">Startable</span><span class=\"p\">,</span>\n <span class=\"k\">private</span> <span class=\"n\">SocketHandler</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的构造函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">RTMFPServer</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">cores</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPServer\"</span><span class=\"p\">),</span>\n <span class=\"n\">_sendingEngine</span><span class=\"p\">(</span><span class=\"n\">cores</span><span class=\"p\">),</span>\n <span class=\"n\">_receivingEngine</span><span class=\"p\">(</span><span class=\"n\">cores</span><span class=\"p\">),</span>\n <span class=\"n\">_pCirrus</span><span class=\"p\">(</span><span class=\"nb\">NULL</span><span class=\"p\">),</span>\n <span class=\"n\">_handshake</span><span class=\"p\">(</span><span class=\"n\">_receivingEngine</span><span class=\"p\">,</span>\n <span class=\"n\">_sendingEngine</span><span class=\"p\">,</span>\n <span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span>\n <span class=\"n\">_edgesSocket</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">,</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">),</span>\n <span class=\"n\">_sessions</span><span class=\"p\">(</span><span class=\"o\">*</span><span class=\"k\">this</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\n\n<table>\n <thead>\n <tr>\n <th>所在线程</th>\n <th>调用者</th>\n <th>函数</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>主线程</td>\n <td>main(…)</td>\n <td> </td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::start()</td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer对象</td>\n <td>Startable::start()</td>\n </tr>\n <tr>\n <td>主线程</td>\n <td>RTMFPServer从Startable继承来的Thread成员</td>\n <td>Thread::start(…)</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\n <td>StartableProcess::run()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::prerun()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>Startable::prerun()</td>\n </tr>\n <tr>\n <td>RTMFPServer</td>\n <td>RTMFPServer对象</td>\n <td>RTMFPServer::run()</td>\n </tr>\n </tbody>\n</table>\n\n<h4 id=\"4rtmfpserver-线程等待\">4、<code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程等待</h4>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">while</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">terminate</span><span class=\"p\">)</span>\n <span class=\"n\">handle</span><span class=\"p\">(</span><span class=\"n\">terminate</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">handle(…)</code> 函数很简单,如下只进行了 <code class=\"language-plaintext highlighter-rouge\">sleep(...)</code> 和 <code class=\"language-plaintext highlighter-rouge\">giveHandle()</code> 两个操作。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">handle</span><span class=\"p\">(</span><span class=\"kt\">bool</span><span class=\"o\">&</span> <span class=\"n\">terminate</span><span class=\"p\">){</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">sleep</span><span class=\"p\">()</span> <span class=\"o\">!=</span> <span class=\"n\">STOP</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">giveHandle</span><span class=\"p\">();</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span>\n <span class=\"n\">terminate</span> <span class=\"o\">=</span> <span class=\"nb\">true</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p><code class=\"language-plaintext highlighter-rouge\">sleep(…)</code> 是 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 是从 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 继承而来的,声明如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">WakeUpType</span> <span class=\"nf\">sleep</span><span class=\"p\">(</span><span class=\"n\">Poco</span><span class=\"o\">::</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">);</span>\n</code></pre></div></div>\n\n<p>定义如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">WakeUpType</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"n\">WakeUpType</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">WAKEUP</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">tryWait</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">))</span>\n <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">TIMEOUT</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>在运行状态下,<code class=\"language-plaintext highlighter-rouge\">_stop</code> 为 <code class=\"language-plaintext highlighter-rouge\">false</code>,而默认参数 <code class=\"language-plaintext highlighter-rouge\">timeout</code> 为 0,所以会调用:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n</code></pre></div></div>\n\n<p>这个 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent</code> 成员是一个 <code class=\"language-plaintext highlighter-rouge\">Poco::Event</code> 对象,<code class=\"language-plaintext highlighter-rouge\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\"language-plaintext highlighter-rouge\">Poco::Event::wait()</code> 后,会一直等待 <code class=\"language-plaintext highlighter-rouge\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\"language-plaintext highlighter-rouge\">wait</code> 的状态。在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 中 <code class=\"language-plaintext highlighter-rouge\">set</code> 的动作是由:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">RTMFPServer::requestHandle()</code></li>\n <li><code class=\"language-plaintext highlighter-rouge\">PoolThread::push(Poco::AutoPtr<RunnableType>& pRunnable)</code></li>\n</ul>\n\n<p>执行的。</p>\n\n<h3 id=\"二rtmfpmanager-对-rtmfpserver-的影响\">二、<code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的影响</h3>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 与 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 同样,继承自 <code class=\"language-plaintext highlighter-rouge\">Startable</code>。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"k\">class</span> <span class=\"nc\">RTMFPManager</span> <span class=\"o\">:</span> <span class=\"k\">private</span> <span class=\"n\">Task</span><span class=\"p\">,</span> <span class=\"k\">private</span> <span class=\"n\">Startable</span>\n</code></pre></div></div>\n\n<p>在构造函数中将 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\"language-plaintext highlighter-rouge\">_server</code> 引用成员。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">RTMFPManager</span><span class=\"p\">(</span><span class=\"n\">RTMFPServer</span><span class=\"o\">&</span> <span class=\"n\">server</span><span class=\"p\">)</span>\n <span class=\"o\">:</span> <span class=\"n\">_server</span><span class=\"p\">(</span><span class=\"n\">server</span><span class=\"p\">),</span>\n <span class=\"n\">Task</span><span class=\"p\">(</span><span class=\"n\">server</span><span class=\"p\">),</span>\n <span class=\"n\">Startable</span><span class=\"p\">(</span><span class=\"s\">\"RTMFPManager\"</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"n\">start</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n\n<span class=\"cm\">/* ...... */</span>\n\n<span class=\"n\">RTMFPServer</span><span class=\"o\">&</span> <span class=\"n\">_server</span><span class=\"p\">;</span>\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的构造函数中调用 <code class=\"language-plaintext highlighter-rouge\">start()</code> 成员函数,是从 <code class=\"language-plaintext highlighter-rouge\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的线程。然后响应到 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager::run()</code> 函数。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">run</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">setPriority</span><span class=\"p\">(</span><span class=\"n\">Thread</span><span class=\"o\">::</span><span class=\"n\">PRIO_LOW</span><span class=\"p\">);</span>\n <span class=\"k\">while</span><span class=\"p\">(</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">2000</span><span class=\"p\">)</span><span class=\"o\">!=</span><span class=\"n\">STOP</span><span class=\"p\">)</span>\n <span class=\"n\">waitHandle</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这里要强调的是,这里的 <code class=\"language-plaintext highlighter-rouge\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\"language-plaintext highlighter-rouge\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\n\n<p>在这里我们可以看到 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的 <code class=\"language-plaintext highlighter-rouge\">handle(…)</code> 中的 <code class=\"language-plaintext highlighter-rouge\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程的 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 的主循环的等待时间的。</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">WakeUpType</span> <span class=\"n\">Startable</span><span class=\"o\">::</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"n\">UInt32</span> <span class=\"n\">timeout</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"n\">WakeUpType</span> <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">WAKEUP</span><span class=\"p\">;</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"o\">></span><span class=\"mi\">0</span><span class=\"p\">)</span> <span class=\"p\">{</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"o\">!</span><span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">tryWait</span><span class=\"p\">(</span><span class=\"n\">timeout</span><span class=\"p\">))</span>\n <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">TIMEOUT</span><span class=\"p\">;</span>\n <span class=\"p\">}</span> <span class=\"k\">else</span> <span class=\"p\">{</span>\n <span class=\"n\">_wakeUpEvent</span><span class=\"p\">.</span><span class=\"n\">wait</span><span class=\"p\">();</span>\n <span class=\"p\">}</span>\n <span class=\"k\">if</span> <span class=\"p\">(</span><span class=\"n\">_stop</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">STOP</span><span class=\"p\">;</span>\n <span class=\"k\">return</span> <span class=\"n\">result</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>你可以自行修改 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 中 <code class=\"language-plaintext highlighter-rouge\">sleep(...)</code> 的参数,这样就会调用 <code class=\"language-plaintext highlighter-rouge\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\"language-plaintext highlighter-rouge\">timeout</code>)来进行睡眠。</p>\n\n<p><code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\"language-plaintext highlighter-rouge\">handle</code> 成员函数:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"nf\">handle</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_server</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>这里就会调用到 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 源码时知道 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer::manage()</code> 函数并不是在 <code class=\"language-plaintext highlighter-rouge\">RTMFPServer</code> 线程内运行的,而是 <code class=\"language-plaintext highlighter-rouge\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\n\n<div class=\"language-c++ highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kt\">void</span> <span class=\"n\">RTMFPServer</span><span class=\"o\">::</span><span class=\"n\">manage</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"n\">_handshake</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n <span class=\"n\">_sessions</span><span class=\"p\">.</span><span class=\"n\">manage</span><span class=\"p\">();</span>\n<span class=\"p\">}</span>\n</code></pre></div></div>\n\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\"language-plaintext highlighter-rouge\">Session</code>。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>我们是应该「断舍离」还是「念念不忘,必有回响」</title>\n \t<meta name=\"description\" content=\"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>我们是应该「断舍离」还是「念念不忘,必有回响」</h2>\t\t\n\t<time datetime=\"2017-01-31T20:59:21+00:00\" class=\"by-line\">31 Jan 2017, 北京 | 作者 麦克船长 | 总计 2577 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"引子\">引子</h3>\n\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\n\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\n\n<p>「嗨,我说老朱,讲讲你啊!」</p>\n\n<p>「我就不提啦。」</p>\n\n<p>「为什么啊?说说,说说!」</p>\n\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\n\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\n\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\n\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\n\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\n\n<blockquote>\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\n</blockquote>\n\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\n\n<h3 id=\"断舍离\">断舍离</h3>\n\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\n\n<p><img src=\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\" alt=\"image\" /></p>\n\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\n\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\n\n<h3 id=\"念念不忘必有回响\">念念不忘,必有回响</h3>\n\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\n\n<p><img src=\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\" alt=\"image\" /></p>\n\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\n\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\n\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\n\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\n\n<h3 id=\"用正负反馈来判断何时何为\">用「正负反馈」来判断何时何为?</h3>\n\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\n\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\n\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\n\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\n\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\n\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\n\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\n\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\n\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\n\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\n\n<h3 id=\"后记\">后记</h3>\n\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\n\n<p>然后,我们一起等待,那声回响。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>未来人工智能就是要:让普通人过上现在富豪们的生活</title>\n \t<meta name=\"description\" content=\"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>未来人工智能就是要:让普通人过上现在富豪们的生活</h2>\t\t\n\t<time datetime=\"2017-02-23T18:23:33+00:00\" class=\"by-line\">23 Feb 2017, 北京 | 作者 麦克船长 | 总计 627 字</time>\n\t<div class=\"content\">\n\t\t<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\n\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\n\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\n\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\n\n<h4 id=\"但是我没有钱呢\">但是我没有钱呢?</h4>\n\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\n\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\n\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\n\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\n\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\n\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\n\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>延迟满足,才有自由</title>\n \t<meta name=\"description\" content=\"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>延迟满足,才有自由</h2>\t\t\n\t<time datetime=\"2020-04-11T06:18:03+00:00\" class=\"by-line\">11 Apr 2020, 杭州 | 作者 麦克船长 | 总计 4478 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\n\n<h3 id=\"1儿童时期的延迟满足\">1、儿童时期的延迟满足</h3>\n\n<h4 id=\"棉花糖实验\">棉花糖实验</h4>\n\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\n\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\n\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\n\n<p>先讲两个我自己的例子吧。</p>\n\n<h4 id=\"黑加仑零食\">黑加仑零食</h4>\n\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\n\n<h4 id=\"暑假作业\">暑假作业</h4>\n\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\n\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\n\n<h4 id=\"延迟满足的背后是意志力自控力\">延迟满足的背后,是意志力/自控力</h4>\n\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\n\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\n\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\n\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\n\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\n\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\n\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\n\n<h3 id=\"2快乐理论挑战延迟满足\">2、快乐理论,挑战延迟满足</h3>\n\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\n\n<h4 id=\"烂苹果\">烂苹果</h4>\n\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\n\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\n\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\n\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\n\n<h4 id=\"快乐理论\">快乐理论</h4>\n\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\n\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\n\n<p>但这个理论角度对吗?</p>\n\n<h3 id=\"3我们应该在哪个范畴内讨论延迟满足\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\n\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\n\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\n\n<h4 id=\"仅有正反馈刺激的奶头乐\">仅有正反馈刺激的奶头乐</h4>\n\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\n\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\n\n<h4 id=\"延迟满足重在顺序而非时间\">延迟满足重在顺序,而非时间</h4>\n\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\n\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\n\n<h3 id=\"4常见的延迟满足与即时满足\">4、常见的延迟满足与即时满足</h3>\n\n<h4 id=\"初阶对手vs浅度延迟满足\">初阶对手 vs 浅度延迟满足</h4>\n\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\n\n<ul>\n <li>\n <p>睡懒觉</p>\n </li>\n <li>\n <p>过量饮食</p>\n </li>\n <li>\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\n </li>\n <li>\n <p>冲动情绪</p>\n </li>\n <li>\n <p>炫耀(粗俗说就是装B)</p>\n </li>\n</ul>\n\n<p>……</p>\n\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\n\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\n\n<h4 id=\"中阶对手vs中度延迟满足\">中阶对手 vs 中度延迟满足</h4>\n\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\n\n<ul>\n <li>\n <p>在家边看电视/视频,边学习/工作</p>\n </li>\n <li>\n <p>依赖二手知识,而怠于一手知识</p>\n </li>\n <li>\n <p>刷知乎、行业资讯 APP</p>\n </li>\n</ul>\n\n<p>……</p>\n\n<p>例子会有很多,我们仅稍微说下这三个。</p>\n\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\n\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\n\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\n\n<h4 id=\"高阶对手vs深度延迟满足\">高阶对手 vs 深度延迟满足</h4>\n\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\n\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\n\n<p>我这里就说一个影响很大的:</p>\n\n<p>* 急于行动</p>\n\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\n\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\n\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\n\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\n\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\n\n<h3 id=\"5延迟满足的驻留时长\">5、延迟满足的驻留时长</h3>\n\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\n\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\n\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\n\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\n\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\n\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\n\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\n\n<h3 id=\"后记\">后记</h3>\n\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\n\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>疫后怎么做餐饮品牌?三叉戟模式或成标配</title>\n \t<meta name=\"description\" content=\"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>疫后怎么做餐饮品牌?三叉戟模式或成标配</h2>\t\t\n\t<time datetime=\"2020-04-14T16:42:43+00:00\" class=\"by-line\">14 Apr 2020, 杭州 | 作者 麦克船长 | 总计 1845 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><img src=\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\n\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\n\n<ul>\n <li>外卖</li>\n <li>做饭</li>\n <li>速食</li>\n</ul>\n\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\n\n<h3 id=\"标品化外卖业务\">标品化外卖业务</h3>\n\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\n\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\n\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\n\n<ul>\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\n</ul>\n\n<h3 id=\"社区生鲜前置仓\">社区生鲜前置仓</h3>\n\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\n\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\n\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\n\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\n\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\n\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\n\n<ul>\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\n</ul>\n\n<h3 id=\"线上预包装食品\">线上预包装食品</h3>\n\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\n\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\n\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\n\n<ul>\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\n</ul>\n\n<h3 id=\"线下重构新餐饮时代到来\">线下重构,新餐饮时代到来</h3>\n\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\n\n<ul>\n <li>用「标品化外卖」覆盖外卖场景</li>\n <li>用「生鲜前置仓」覆盖做饭场景</li>\n <li>用「预包装食品」覆盖速食场景</li>\n</ul>\n\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\n\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\n\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>担任淘宝产品总负责人的双十一,是怎样的体验?</title>\n \t<meta name=\"description\" content=\"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>担任淘宝产品总负责人的双十一,是怎样的体验?</h2>\t\t\n\t<time datetime=\"2020-11-11T15:59:43+00:00\" class=\"by-line\">11 Nov 2020, 杭州 | 作者 麦克船长 | 总计 138 字</time>\n\t<div class=\"content\">\n\t\t<p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\n\n<p><img src=\"/img/src/2020-11-11-captain-double-eleven-1.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-2.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-3.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-4.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-5.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-6.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-7.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-8.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-9.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-10.jpg\" alt=\"image\" />\n<img src=\"/img/src/2020-11-11-captain-double-eleven-11.jpg\" alt=\"image\" /></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的阿里一年香(入职阿里一周年)</title>\n \t<meta name=\"description\" content=\"本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的阿里一年香(入职阿里一周年)</h2>\t\t\n\t<time datetime=\"2021-06-04T15:42:43+00:00\" class=\"by-line\">04 Jun 2021, 杭州 | 作者 麦克船长 | 总计 304 字</time>\n\t<div class=\"content\">\n\t\t<p>To 钟超</p>\n\n<p>1 周年快乐!很开心我们有这样一段共事的机会,虽开始时有些许波折,但随着进一步相处,我们很快能做到彼此欣赏、英雄相惜、默契配合,也特别感谢你对我的信任和支持,这是一切共事的基础。你强大的自驱力、脑力、对新事物的理解学习能力,都是最近几手新人里比较突出的。特别钦佩于你的执着和初性,对一件事认定后,迸发出的强大战斗力和决心。今天特卖这个新业务需要扎下根基,还真的需要一些舍我其谁的胆魄和更为犀利的突破,我也相信「新特卖」能成为你在阿里又一代表作,我希望我们的团队能为之骄傲和自豪,我们能不负公司所托,真正在下沉市场这场硬仗上有所建树,井取得令我们自己感到骄傲的突破,一起加油。</p>\n\n<p>From 麦克船长的主管</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</title>\n \t<meta name=\"description\" content=\"阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</h2>\t\t\n\t<time datetime=\"2021-11-11T19:59:43+00:00\" class=\"by-line\">11 Nov 2021, 杭州 | 作者 麦克船长 | 总计 917 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2021-11-11-captain-tttm-1.jpg\" alt=\"imagee\" /></p>\n\n<h3 id=\"天天特卖团队理念\">天天特卖团队理念</h3>\n\n<h4 id=\"特卖合伙人\">特卖合伙人</h4>\n\n<p>以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。特卖核心管理团队每 Q 会进行一次班子建设通晒。</p>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-9.jpg\" alt=\"imagee\" /></p>\n\n<h4 id=\"如何理解协作\">如何理解协作?</h4>\n\n<p>从长时间线来看,我们是为了不断积累信用,像一张信用卡一样,不断获得别人愿意支持我们的更大额度。不要事情结果还可以,而我们却没有积累到信用。互联网本质也是现代工业。而现代工业,一是社会分工,二是社会协作。想取得现代工业项目的结果,就要有更大的人才包容度、环境包容度。工作的结果就是在妥协与博弈中取得的,这是和光同尘的本质,也是现代工业复杂系统拿到结果的本质。只有这样我们才能让越来越多的人追随我们一起 do something,这种追随不一定只有上下级才是,而是愿意并且相信和我们能到达更远的地方。这背后的信用,要我们一步一个脚印地去积累,对他人给予的信任要保持敬畏、如履薄冰、懂得感恩。对每一段阶段性或长或短结束的合作,都要表达感谢。</p>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-8.jpg\" alt=\"imagee\" /></p>\n\n<h4 id=\"如何看待同学的优势及短板\">如何看待同学的优势及短板?</h4>\n\n<ul>\n <li>优势:讲优势有两个可能的目的,要么组织会在未来任务分配上重点考虑发挥该同学优势的事情,要告诉 TA,要激励 TA,是 TA 前行的自信来源之一。要么是对于同学也把握不准的特点,我们明确告诉 TA 这是你被我欣赏的优点。</li>\n <li>短板:什么是要讲的短板?未来一段时间,最期待你补足提升的。一旦这方面显著进步,就会向上迈进很大一步,甚至可以突破自己当下成长的瓶颈。要花多少篇幅讲?要比优势,有更大篇幅去讲。讲完就结束了么?对这个短板,一定要表达态度,也一定要对是否有方法、什么方法来补足短板要和同学沟通。</li>\n <li>无论是优势,还是短板,要说到点儿上,不要说片儿汤话。要让同学们能够引起思考、启发的。</li>\n</ul>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-10.jpg\" alt=\"imagee\" /></p>\n\n<h3 id=\"天天特卖期待你的加入\">天天特卖期待你的加入!</h3>\n\n<p>新天天特卖缘起于「手淘下沉市场战役)」,于 2021 年初上线,以「极致性价比货源、裸价直降、全网比价、买贵必赔」打造手淘极致价格敏感人群的购物阵地。目前天天特卖团队有行业运营、用户运营、数据策略、整合营销、直播运营、内容运营等岗位,有兴趣的同学可以钉钉随时找我,期待你的加入!</p>\n\n<h4 id=\"欢迎添加我的微信sinosuperman-推荐自荐--\">欢迎添加我的微信:sinosuperman 推荐、自荐 ^ ^</h4>\n\n<p><img src=\"/img/src/2021-11-11-captain-tttm-11.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-2.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-3.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-4.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-5.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-6.jpg\" alt=\"imagee\" />\n<img src=\"/img/src/2021-11-11-captain-tttm-7.jpg\" alt=\"imagee\" /></p>\n\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\n \t<meta name=\"description\" content=\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\t\t\n\t<time datetime=\"2021-12-21T15:53:57+00:00\" class=\"by-line\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#写在前面\" id=\"markdown-toc-写在前面\">写在前面</a></li>\n <li><a href=\"#1github上的准备\" id=\"markdown-toc-1github上的准备\">1、GitHub 上的准备</a></li>\n <li><a href=\"#2了解ruby和jekyll\" id=\"markdown-toc-2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</a></li>\n <li><a href=\"#3了解gem\" id=\"markdown-toc-3了解gem\">3、了解 Gem</a></li>\n <li><a href=\"#4安装homebrew\" id=\"markdown-toc-4安装homebrew\">4、安装 Homebrew</a></li>\n <li><a href=\"#5用homebrew安装ruby\" id=\"markdown-toc-5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</a></li>\n <li><a href=\"#6安装jekyll和bundler\" id=\"markdown-toc-6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</a></li>\n <li><a href=\"#7使用bundle管理包依赖关系\" id=\"markdown-toc-7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</a></li>\n <li><a href=\"#8本地启动一下看看\" id=\"markdown-toc-8本地启动一下看看\">8、本地启动一下看看</a></li>\n <li><a href=\"#9用jekyll创建一个项目\" id=\"markdown-toc-9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</a></li>\n <li><a href=\"#10修改gemfile文件\" id=\"markdown-toc-10修改gemfile文件\">10、修改 Gemfile 文件</a></li>\n <li><a href=\"#11配置githubpages\" id=\"markdown-toc-11配置githubpages\">11、配置 Github Pages</a></li>\n <li><a href=\"#12配置一个jekylltheme\" id=\"markdown-toc-12配置一个jekylltheme\">12、配置一个 Jekyll Theme</a></li>\n <li><a href=\"#13设置自定义域名\" id=\"markdown-toc-13设置自定义域名\">13、设置自定义域名</a></li>\n <li><a href=\"#14用-rouge-实现代码高亮\" id=\"markdown-toc-14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</a></li>\n <li><a href=\"#15一些扩展问题\" id=\"markdown-toc-15一些扩展问题\">15、一些扩展问题</a> <ul>\n <li><a href=\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\" id=\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\n <li><a href=\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\" id=\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\n <li><a href=\"#q3如何为每篇文章添加一个目录\" id=\"markdown-toc-q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</a></li>\n <li><a href=\"#q4如何在-jekyll-中支持-katex\" id=\"markdown-toc-q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\n <li><a href=\"#在-githubio-上\" id=\"markdown-toc-在-githubio-上\">在 GitHub.io 上</a></li>\n <li><a href=\"#如果不在-githubio-上则还需要额外工作\" id=\"markdown-toc-如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\n <li><a href=\"#使用示例\" id=\"markdown-toc-使用示例\">使用示例</a></li>\n </ul>\n </li>\n <li><a href=\"#q5jekyll-中如何支持-graphviz-\" id=\"markdown-toc-q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\n <li><a href=\"#q6如何显示--或者--\" id=\"markdown-toc-q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</a></li>\n </ul>\n </li>\n <li><a href=\"#参考\" id=\"markdown-toc-参考\">参考</a></li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\n\n<ul>\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\n</ul>\n\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\n\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\n\n<h3 id=\"1github上的准备\">1、GitHub 上的准备</h3>\n\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git clone https://github.com/username/username.github.io\n</code></pre></div></div>\n\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git add <span class=\"nt\">--all</span>\n<span class=\"nv\">$ </span>git commit <span class=\"nt\">-m</span> <span class=\"s2\">\"Initial commit\"</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"2了解ruby和jekyll\">2、了解 Ruby 和 Jekyll</h3>\n\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\n\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\n\n<h3 id=\"3了解gem\">3、了解 Gem</h3>\n\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\n\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\ngem sources -l\n</code></pre></div></div>\n\n<h3 id=\"4安装homebrew\">4、安装 Homebrew</h3>\n\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>/bin/bash <span class=\"nt\">-c</span> <span class=\"s2\">\"</span><span class=\"si\">$(</span>curl <span class=\"nt\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\"si\">)</span><span class=\"s2\">\"</span>\n</code></pre></div></div>\n\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">echo</span> <span class=\"s1\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\"o\">>></span> ~/.bash_profile\n</code></pre></div></div>\n\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\n\n<h3 id=\"5用homebrew安装ruby\">5、用 Homebrew 安装 Ruby</h3>\n\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>brew <span class=\"nb\">install </span>chruby ruby-install xz\n</code></pre></div></div>\n\n<p>安装 Ruby 的最新版本:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby-install ruby\n</code></pre></div></div>\n\n<p>这时候提示如下问题:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"o\">>>></span> Updating ruby versions ...\n<span class=\"o\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\"se\">\\</span>\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\n<span class=\"o\">!!!</span> Failed to download ruby versions!\n</code></pre></div></div>\n\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ echo \"185.199.111.133 raw.githubusercontent.com\" >> /etc/hosts\n</code></pre></div></div>\n\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/chruby.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"source </span><span class=\"si\">$(</span>brew <span class=\"nt\">--prefix</span><span class=\"si\">)</span><span class=\"s2\">/opt/chruby/share/chruby/auto.sh\"</span> <span class=\"o\">>></span> ~/.zshrc\n<span class=\"nv\">$ </span><span class=\"nb\">echo</span> <span class=\"s2\">\"chruby ruby-3.1.2\"</span> <span class=\"o\">>></span> ~/.zshrc <span class=\"c\"># run 'chruby' to see actual version</span>\n</code></pre></div></div>\n\n<p>再看下 Ruby 版本对不对:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>ruby <span class=\"nt\">-v</span>\n</code></pre></div></div>\n\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\n\n<h3 id=\"6安装jekyll和bundler\">6、安装 Jekyll 和 Bundler</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"nb\">install </span>jekyll bundler\n</code></pre></div></div>\n\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\n\n<h3 id=\"7使用bundle管理包依赖关系\">7、使用 bundle 管理包依赖关系</h3>\n\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nb\">source</span> <span class=\"s1\">'https://rubygems.org'</span>\ngem <span class=\"s1\">'nokogiri'</span>\ngem <span class=\"s1\">'rack'</span>, <span class=\"s1\">'~> 2.2.4'</span>\ngem <span class=\"s1\">'rspec'</span>\ngem <span class=\"s1\">'jekyll'</span>\n</code></pre></div></div>\n\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">install</span>\n</code></pre></div></div>\n\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ git add Gemfile Gemfile.lock\n</code></pre></div></div>\n\n<h3 id=\"8本地启动一下看看\">8、本地启动一下看看</h3>\n\n<p>先用 bundle 如下命令来启动:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: none\n Source: /Users/captain/Workspace/poechant.github.io\n Destination: /Users/captain/Workspace/poechant.github.io/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n done in 0.014 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\n Server address: http://127.0.0.1:4000\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\" alt=\"image\" /></p>\n\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>git pull <span class=\"nt\">--no-rebase</span>\n<span class=\"nv\">$ </span>git push <span class=\"nt\">-u</span> origin main\n</code></pre></div></div>\n\n<h3 id=\"9用jekyll创建一个项目\">9、用 Jekyll 创建一个项目</h3>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll new CaptainMikeBlog\n<span class=\"nv\">$ </span><span class=\"nb\">cd </span>CaptainMikeBlog\n<span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>启动日志如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\n Incremental build: disabled. Enable with --incremental\n Generating... \n Jekyll Feed: Generating feed for posts\n done in 0.365 seconds.\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\n Server address: http://127.0.0.1:4000/\n Server running... press ctrl-c to stop.\n</code></pre></div></div>\n\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\n\n<p><img src=\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\" alt=\"image\" /></p>\n\n<h3 id=\"10修改gemfile文件\">10、修改 Gemfile 文件</h3>\n\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>gem <span class=\"s2\">\"github-pages\"</span>, <span class=\"s2\">\"~> GITHUB-PAGES-VERSION\"</span>, group: :jekyll_plugins\n</code></pre></div></div>\n\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>$ bundle install\n</code></pre></div></div>\n\n<p>再本地启动服务器测试:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>jekyll server\n</code></pre></div></div>\n\n<p>得到如下提示:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\nPrepending <span class=\"sb\">`</span>bundle <span class=\"nb\">exec</span><span class=\"sb\">`</span> to your <span class=\"nb\">command </span>may solve this. <span class=\"o\">(</span>Gem::LoadError<span class=\"o\">)</span>\n</code></pre></div></div>\n\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>bundle add webrick\n<span class=\"nv\">$ </span>bundle <span class=\"nb\">exec </span>jekyll serve\n</code></pre></div></div>\n\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\n\n<h3 id=\"11配置githubpages\">11、配置 Github Pages</h3>\n\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>baseurl: \"\"\nurl: \"http://your-username.github.io\"\n</code></pre></div></div>\n\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\n\n<h3 id=\"12配置一个jekylltheme\">12、配置一个 Jekyll Theme</h3>\n\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>_includes\n_layouts\n_sass\ncss\njs\nimg\n404.markdown\nindex.html\n</code></pre></div></div>\n\n<p>不同主题会有所不同,这里只列个大概。</p>\n\n<h3 id=\"13设置自定义域名\">13、设置自定义域名</h3>\n\n<p>添加四条 A 记录,记录值如下:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>185.199.108.153\n185.199.109.153\n185.199.110.153\n185.199.111.153\n</code></pre></div></div>\n\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\n\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\n\n<h3 id=\"14用-rouge-实现代码高亮\">14、用 rouge 实现代码高亮</h3>\n\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem install kramdom rouge\n</code></pre></div></div>\n\n<p>然后配置 _config.yml 文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>然后用 rouge 创建 syntax.css 文件:</p>\n\n<div class=\"language-bash highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nv\">$ </span>rougify style github <span class=\"o\">></span> css/syntax.css\n</code></pre></div></div>\n\n<p>在 <code class=\"language-plaintext highlighter-rouge\">_include/head.html</code> 文件中添加:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span> <span class=\"na\">href=</span><span class=\"s\">\"/css/syntax.css\"</span> <span class=\"nt\">/></span>\n</code></pre></div></div>\n\n<h3 id=\"15一些扩展问题\">15、一些扩展问题</h3>\n\n<h4 id=\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\n\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">这是一篇文章</span>\n<span class=\"na\">excerpt</span><span class=\"pi\">:</span> <span class=\"s\">这是文章的摘要</span>\n<span class=\"nn\">---</span>\n\n这是文章的正文内容\n</code></pre></div></div>\n\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><ul></span>\n {% for post in paginator.posts %}\n <span class=\"nt\"><li></span>\n <span class=\"nt\"><h2><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{{ post.url }}\"</span><span class=\"nt\">></span>{{ post.title }}<span class=\"nt\"></a></h2></span>\n <span class=\"nt\"><p></span>{{ post.excerpt }}<span class=\"nt\"></p></span>\n <span class=\"nt\"></li></span>\n {% endfor %}\n<span class=\"nt\"></ul></span>\n</code></pre></div></div>\n\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\n\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\n\n<h4 id=\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\n\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\"language-plaintext highlighter-rouge\">_layouts</code>目录下创建一个<code class=\"language-plaintext highlighter-rouge\">category.html</code>文件:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>---\nlayout: default\n---\n\n<span class=\"nt\"><div</span> <span class=\"na\">class=</span><span class=\"s\">\"container\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><br></span>\n {% if site.categories[page.category] %}\n {% for post in site.categories[page.category] %}\n <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"{% if site.baseurl == \"</span><span class=\"err\">/\"</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">}}{%</span> <span class=\"na\">else</span> <span class=\"err\">%}{{</span> <span class=\"na\">post.url</span> <span class=\"err\">|</span> <span class=\"na\">prepend:</span> <span class=\"na\">site.baseurl</span> <span class=\"err\">}}{%</span> <span class=\"na\">endif</span> <span class=\"err\">%}\"</span><span class=\"nt\">></span>\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\n <span class=\"nt\"></a></span>\n {% endfor %}\n {% else %}\n <span class=\"nt\"><br></span>\n <span class=\"nt\"><p></span>No posts for this category. If you have something in mind, check <span class=\"nt\"><a</span> <span class=\"na\">href=</span><span class=\"s\">\"/write\"</span><span class=\"nt\">></span>Write For Us<span class=\"nt\"></a></span>page.<span class=\"nt\"></p></span>\n {% endif %}\n<span class=\"nt\"></div></span>\n</code></pre></div></div>\n\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\"language-plaintext highlighter-rouge\">_config.yml</code>文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">include</span><span class=\"pi\">:</span> <span class=\"pi\">[</span><span class=\"s1\">'</span><span class=\"s\">_categories'</span><span class=\"pi\">]</span>\n</code></pre></div></div>\n\n<p>在根目录创建一个<code class=\"language-plaintext highlighter-rouge\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\"language-plaintext highlighter-rouge\">ai.html</code>如下:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">category</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">人工智能</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s\">This is the description.</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/category/ai</span>\n<span class=\"na\">category</span><span class=\"pi\">:</span> <span class=\"s\">ai</span>\n<span class=\"na\">category_type</span><span class=\"pi\">:</span> <span class=\"s\">tech</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>那么之后每次创建文件时,在头部写<code class=\"language-plaintext highlighter-rouge\">category</code>一定要与这些<code class=\"language-plaintext highlighter-rouge\">categories</code>中的<code class=\"language-plaintext highlighter-rouge\">html</code>文件对应起来。</p>\n\n<h4 id=\"q3如何为每篇文章添加一个目录\">Q3:如何为每篇文章添加一个目录</h4>\n\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"p\">*</span> TOC\n{:toc}\n</code></pre></div></div>\n\n<h4 id=\"q4如何在-jekyll-中支持-katex\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\n\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\n\n<h5 id=\"在-githubio-上\">在 GitHub.io 上</h5>\n\n<p>先修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code>:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">math_engine</span><span class=\"pi\">:</span> <span class=\"s\">katex</span>\n</code></pre></div></div>\n\n<p>然后修改 <code class=\"language-plaintext highlighter-rouge\">_includes/head.html</code> 文件,在 <code class=\"language-plaintext highlighter-rouge\"><head></code> 与 <code class=\"language-plaintext highlighter-rouge\"></head></code> 中间:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"><!--KaTeX--></span>\n <span class=\"nt\"><link</span> <span class=\"na\">rel=</span><span class=\"s\">\"stylesheet\"</span>\n <span class=\"na\">href=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script </span><span class=\"na\">defer</span>\n <span class=\"na\">src=</span><span class=\"s\">\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\"</span>\n <span class=\"na\">integrity=</span><span class=\"s\">\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\"</span>\n <span class=\"na\">crossorigin=</span><span class=\"s\">\"anonymous\"</span><span class=\"nt\">></script></span>\n <span class=\"nt\"><script></span>\n <span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">addEventListener</span><span class=\"p\">(</span><span class=\"dl\">\"</span><span class=\"s2\">DOMContentLoaded</span><span class=\"dl\">\"</span><span class=\"p\">,</span> <span class=\"kd\">function</span><span class=\"p\">()</span> <span class=\"p\">{</span>\n <span class=\"nx\">renderMathInElement</span><span class=\"p\">(</span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">body</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n <span class=\"c1\">// ...options...</span>\n <span class=\"p\">});</span>\n <span class=\"p\">});</span>\n <span class=\"nt\"></script></span>\n</code></pre></div></div>\n\n<h5 id=\"如果不在-githubio-上则还需要额外工作\">如果不在 GitHub.io 上,则还需要额外工作</h5>\n\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>gem <span class=\"nb\">install </span>kramdom-math-katex\n\ngem <span class=\"nb\">install </span>katex\ngem <span class=\"nb\">install </span>execjs\n\ngem <span class=\"nb\">install </span>therubyracer\ngem <span class=\"nb\">install </span>therubyrhino\ngem <span class=\"nb\">install </span>duktape\n</code></pre></div></div>\n\n<h5 id=\"使用示例\">使用示例</h5>\n\n<p>以如下方式输入输入如下内容:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}\n$$ \\sum_{i=1}^{n} a_i $$\n{% endraw %}\n</code></pre></div></div>\n\n<p>就会得到一个数学公式:</p>\n\n\\[\\sum_{i=1}^{n} a_i\\]\n\n<h4 id=\"q5jekyll-中如何支持-graphviz-\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\n\n<p>这要依赖 <code class=\"language-plaintext highlighter-rouge\">jekyll-graphviz-dot</code>,修改 <code class=\"language-plaintext highlighter-rouge\">Gemfile</code> 增加一句:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>group :jekyll_plugins <span class=\"k\">do\n </span>gem <span class=\"s2\">\"jekyll-graphviz-dot\"</span>\nend\n</code></pre></div></div>\n\n<p>再修改 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 配置文件:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-graphviz</span>\n</code></pre></div></div>\n\n<p>再在本地安装 graphviz,可以通过 <code class=\"language-plaintext highlighter-rouge\">conda install graphviz</code> 或者 <code class=\"language-plaintext highlighter-rouge\">brew install graphviz</code>。然后 <code class=\"language-plaintext highlighter-rouge\">bundle install</code> 再 <code class=\"language-plaintext highlighter-rouge\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\n\n<pre><code class=\"language-graphviz\">{% graph some graph title %}\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n{% endgraph %}\n</code></pre>\n\n<p>如果看到如下效果,就说明你都配置成功了:</p>\n\n<div class=\"graphviz-wrapper\">\n\n<!-- Generated by graphviz version 2.43.0 (0)\n -->\n<!-- Title: G Pages: 1 -->\n<svg role=\"img\" aria-label=\"some graph title\" width=\"89pt\" height=\"188pt\" viewBox=\"0.00 0.00 89.00 188.00\">\n<title>some graph title</title>\n<desc>\ndigraph G {\n a -> b\n b -> c\n c -> a\n}\n</desc>\n\n<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n<title>G</title>\n<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-184 85,-184 85,4 -4,4\" />\n<!-- a -->\n<g id=\"node1\" class=\"node\">\n<title>a</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-162\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">a</text>\n</g>\n<!-- b -->\n<g id=\"node2\" class=\"node\">\n<title>b</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">b</text>\n</g>\n<!-- a->b -->\n<g id=\"edge1\" class=\"edge\">\n<title>a->b</title>\n<path fill=\"none\" stroke=\"black\" d=\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\" />\n</g>\n<!-- c -->\n<g id=\"node3\" class=\"node\">\n<title>c</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"54\" cy=\"-18\" rx=\"27\" ry=\"18\" />\n<text text-anchor=\"middle\" x=\"54\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">c</text>\n</g>\n<!-- b->c -->\n<g id=\"edge2\" class=\"edge\">\n<title>b->c</title>\n<path fill=\"none\" stroke=\"black\" d=\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\" />\n</g>\n<!-- c->a -->\n<g id=\"edge3\" class=\"edge\">\n<title>c->a</title>\n<path fill=\"none\" stroke=\"black\" d=\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\" />\n<polygon fill=\"black\" stroke=\"black\" points=\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\" />\n</g>\n</g>\n</svg>\n</div>\n\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\n\n<h4 id=\"q6如何显示--或者--\">Q6:如何显示 <code class=\"language-plaintext highlighter-rouge\">{%</code> 或者 <code class=\"language-plaintext highlighter-rouge\">{{</code> ?</h4>\n\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 呢?方法如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{% raw %}{%{% endraw %} raw %}\n{% raw %}{%{% endraw %} endraw %}\n</code></pre></div></div>\n\n<p>如上,就是用 <code class=\"language-plaintext highlighter-rouge\">{% raw %}</code> 和 <code class=\"language-plaintext highlighter-rouge\">{% endraw %}</code> 把 <code class=\"language-plaintext highlighter-rouge\">{%</code> 包起来,但是 <code class=\"language-plaintext highlighter-rouge\">%}</code> 不用包。应该讲的很清楚了吧。</p>\n\n<h3 id=\"参考\">参考</h3>\n\n<ol>\n <li><a href=\"https://bundler.io\">https://bundler.io</a></li>\n <li><a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></li>\n <li><a href=\"https://zhuanlan.zhihu.com/p/87225594\">https://zhuanlan.zhihu.com/p/87225594</a></li>\n <li><a href=\"https://chat.openai.com/chat\">https://chat.openai.com/chat</a></li>\n <li><a href=\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\n <li><a href=\"https://github.com/dyutibarma/monochrome\">https://github.com/dyutibarma/monochrome</a></li>\n <li><a href=\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\n <li><a href=\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\n <li><a href=\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n <li><a href=\"https://github.com/DerekStride/jekyll-graphviz\">https://github.com/DerekStride/jekyll-graphviz</a></li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>麦克船长的 Jekyll 快速教程</title>\n \t<meta name=\"description\" content=\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>麦克船长的 Jekyll 快速教程</h2>\t\t\n\t<time datetime=\"2021-12-23T19:43:02+00:00\" class=\"by-line\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<h3 id=\"写在前面\">写在前面</h3>\n\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\n\n<h3 id=\"part-1基本特点\">Part 1、基本特点</h3>\n\n<h4 id=\"一基本语法\">一、基本语法</h4>\n\n<ul>\n <li>变量:用双大括号表示变量 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code></li>\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\"language-plaintext highlighter-rouge\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\n <li>支持循环与分支结构:比如 <code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 和 <code class=\"language-plaintext highlighter-rouge\">if-elsif-else-endif</code> :可以使用 <code class=\"language-plaintext highlighter-rouge\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\n</ul>\n\n<h4 id=\"二典型-jekyll-项目结构及重要文件介绍\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\n\n<h5 id=\"1配置文件-_configyml\">1、配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code></h5>\n\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\"language-plaintext highlighter-rouge\">encoding: utf-8</code> 这一条。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Site settings</span>\n<span class=\"na\">encoding</span><span class=\"pi\">:</span> <span class=\"s\">utf-8</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">麦克船长的技术、产品与商业博客</span>\n<span class=\"na\">description</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\"</span>\n<span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptain.com\"</span>\n<span class=\"na\">author</span><span class=\"pi\">:</span>\n <span class=\"na\">name</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Your</span><span class=\"nv\"> </span><span class=\"s\">Name\"</span>\n <span class=\"na\">url</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">https://www.mikecaptian.com\"</span>\n</code></pre></div></div>\n\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Markdown and highlighter</span>\n<span class=\"na\">markdown</span><span class=\"pi\">:</span> <span class=\"s\">kramdown</span>\n<span class=\"na\">highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n<span class=\"na\">kramdown</span><span class=\"pi\">:</span>\n <span class=\"na\">input</span><span class=\"pi\">:</span> <span class=\"s\">GFM</span>\n <span class=\"na\">syntax_highlighter</span><span class=\"pi\">:</span> <span class=\"s\">rouge</span>\n</code></pre></div></div>\n\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Plugins</span>\n<span class=\"na\">plugins</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-paginate</span>\n <span class=\"pi\">-</span> <span class=\"s\">jekyll-sitemap</span>\n</code></pre></div></div>\n\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c1\"># Build settings</span>\n<span class=\"na\">baseurl</span><span class=\"pi\">:</span> <span class=\"c1\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\n<span class=\"na\">source</span><span class=\"pi\">:</span> <span class=\"s\">.</span>\n<span class=\"na\">destination</span><span class=\"pi\">:</span> <span class=\"s\">./_site</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:title</span>\n<span class=\"na\">paginate</span><span class=\"pi\">:</span> <span class=\"m\">20</span>\n<span class=\"na\">paginate_path</span><span class=\"pi\">:</span> <span class=\"s\">/page:num/</span>\n<span class=\"na\">collections</span><span class=\"pi\">:</span>\n <span class=\"na\">posts</span><span class=\"pi\">:</span>\n <span class=\"na\">output</span><span class=\"pi\">:</span> <span class=\"no\">true</span>\n <span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/:year/:month/:day/:title/</span>\n <span class=\"na\">directory</span><span class=\"pi\">:</span> <span class=\"s\">_posts</span>\n</code></pre></div></div>\n\n<h5 id=\"2布局文件_layouts-目录下的文件规则\">2、布局文件:<code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的文件规则</h5>\n\n<p>Jekyll 的 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\"language-plaintext highlighter-rouge\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\"language-plaintext highlighter-rouge\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\n\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\"language-plaintext highlighter-rouge\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\n\n<h5 id=\"3页面文件及其头部\">3、页面文件及其头部</h5>\n\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\n\n<div class=\"language-markdown highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nn\">---</span>\n<span class=\"na\">layout</span><span class=\"pi\">:</span> <span class=\"s\">page</span>\n<span class=\"na\">permalink</span><span class=\"pi\">:</span> <span class=\"s\">/categories/</span>\n<span class=\"na\">title</span><span class=\"pi\">:</span> <span class=\"s\">Categories</span>\n<span class=\"nn\">---</span>\n</code></pre></div></div>\n\n<p>每个页面文件的头部都会有layout,并与 <code class=\"language-plaintext highlighter-rouge\">_layouts</code> 目录下的某个文件对应。</p>\n\n<h3 id=\"part-2jekyll-中的全局变量\">Part 2、Jekyll 中的全局变量</h3>\n\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:包含当前页面或文章的内容。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:包含有关网站的所有标签的信息。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\n</ul>\n\n<p>这些变量可以在模板中使用,比如:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"nt\"><h1></span>{{ page.title }}<span class=\"nt\"></h1></span>\n<span class=\"nt\"><p></span>{{ site.description }}<span class=\"nt\"></p></span>\n<span class=\"nt\"><ul></span>\n {{ for category in site.categories %}\n <span class=\"nt\"><li></span>{{ category }}<span class=\"nt\"></li></span>\n {{ endfor %}\n<span class=\"nt\"></ul></span> \n</code></pre></div></div>\n\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\"language-plaintext highlighter-rouge\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">my_custom_variable</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">Hello</span><span class=\"nv\"> </span><span class=\"s\">World\"</span>\n</code></pre></div></div>\n\n<p>然后就可以在模板文件中使用 <code class=\"language-plaintext highlighter-rouge\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\n\n<h4 id=\"一site变量\">一、site变量</h4>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\n\n<h5 id=\"1sitecategories\">1、<code class=\"language-plaintext highlighter-rouge\">site.categories</code></h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\"language-plaintext highlighter-rouge\">first</code> 就是 <code class=\"language-plaintext highlighter-rouge\">category</code> 的名字,如下使用:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>\n</code></pre></div></div>\n\n<h5 id=\"2sitepages\">2、site.pages</h5>\n\n<p><code class=\"language-plaintext highlighter-rouge\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\"language-plaintext highlighter-rouge\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3其他常用属性\">3、其他常用属性</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">site.title</code>:是网站的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.related_posts</code>:相关文章的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.data</code>:从 <code class=\"language-plaintext highlighter-rouge\">_data</code> 目录加载的数据。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.static_files</code>:静态文件的列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">site.collections</code>:自定义集合的列表。</li>\n</ul>\n\n<h4 id=\"二page-变量\">二、<code class=\"language-plaintext highlighter-rouge\">page</code> 变量</h4>\n\n<p>在 Jekyll 中,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量属性包括:</p>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">layout</code>:表示页面使用的布局模板的名称。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">title</code>:表示页面的标题。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">date</code>:表示页面的发布日期。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">categories</code>:表示页面所属的分类列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">tags</code>:表示页面所属的标签列表。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\n</ul>\n\n<p>在模板中,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.属性名 }}</code> 的方式来访问 <code class=\"language-plaintext highlighter-rouge\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\"language-plaintext highlighter-rouge\">{{ page.title }}</code>。此外,<code class=\"language-plaintext highlighter-rouge\">page</code> 变量还有其他属性,如 <code class=\"language-plaintext highlighter-rouge\">permalink</code>、<code class=\"language-plaintext highlighter-rouge\">excerpt</code>、<code class=\"language-plaintext highlighter-rouge\">url</code> 等,可以根据需要调用。</p>\n\n<h3 id=\"part-3控制结构\">Part 3、控制结构</h3>\n\n<h4 id=\"1if-else-分支结构\">1、<code class=\"language-plaintext highlighter-rouge\">if-else</code> 分支结构</h4>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ if tmp_var == \"type1\" %}\n{{ elsif tmp_var == \"type2\" %}\n{{ elsif tmp_var == \"type3\" %}\n{{ elsif tmp_var == \"type4\" %}\n{{ else tmp_var == \"type5\" %}\n{{ endif %}\n</code></pre></div></div>\n\n<h4 id=\"2for-endfor-循环结构\">2、<code class=\"language-plaintext highlighter-rouge\">for-endfor</code> 循环结构</h4>\n\n<p>不带条件判断的 <code class=\"language-plaintext highlighter-rouge\">for</code> 循环如下:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for post in paginator.posts %}\n\t<span class=\"c\"><!-- Your other sentences --></span>\n{{ endfor %}\n</code></pre></div></div>\n\n<p>带条件循环的 <code class=\"language-plaintext highlighter-rouge\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\n\n<div class=\"language-html highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>{{ for page in site.pages | where: \"dir\", \"categories\" %}\n\t{{ page.title }}\n{{ endfor %}\n</code></pre></div></div>\n\n<h5 id=\"3jekyll-支持的其他结构包括\">3、Jekyll 支持的其他结构包括:</h5>\n\n<ul>\n <li><code class=\"language-plaintext highlighter-rouge\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">capture</code> 用于捕获输出的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">cycle</code> 用于循环一组字符串的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">include</code> 用于包含其他文件的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\n <li><code class=\"language-plaintext highlighter-rouge\">while</code> 用于在满足指定条件时执行代码的结构。</li>\n</ul>\n\n<h3 id=\"参考\">参考:</h3>\n\n<p>1、<a href=\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\n2、<a href=\"https://jekyllrb.com/docs/\">https://jekyllrb.com/docs/</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>不要船开远了,就忘了为什么启航</title>\n \t<meta name=\"description\" content=\"2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>不要船开远了,就忘了为什么启航</h2>\t\t\n\t<time datetime=\"2022-08-11T15:53:57+00:00\" class=\"by-line\">11 Aug 2022, 杭州 | 作者 麦克船长 | 总计 3223 字</time>\n\t<div class=\"content\">\n\t\t<h3 id=\"写在前面\">写在前面</h3>\n<p>偶然翻到 2020.06.11 刚来到阿里时写的一篇内容(我是 2020 年的 6 月 4 日我入职阿里巴巴集团),是有关于来阿里的期待、对这家公司的一些粗浅初步的理解。此时再翻来看看,最大的感触就是,提醒自己勿忘初心。</p>\n\n<p>在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:</p>\n\n<ul>\n <li>很多人是带着梦想来阿里的,那么我的梦想是什么呢?</li>\n <li>最喜欢新六脉的哪句话?为什么?</li>\n <li>关于阿里企业价值观:为什么要接受这套价值观?</li>\n <li>价值观的本质意义(极度务实视角)是什么?</li>\n <li>Landing 的 SOP</li>\n <li>问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-1.png\" alt=\"image\" /></p>\n\n<p>注:2020 年平安夜 · 百年湖畔 87 期合影</p>\n\n<h3 id=\"很多人是带着梦想来阿里的那么我的梦想是什么呢\">很多人是带着梦想来阿里的,那么我的梦想是什么呢?</h3>\n\n<p>Christensen 在《创新者的窘境》中提到:每一次技术更迭,都需要破坏性创新,而破坏性创新在前一次技术更迭的胜出者内部是很难生长出来的。阿里诞生以来,不断地创造第二增长曲线:阿里巴巴、淘宝、支付宝、天猫、阿里云、钉钉 …… 这让我非常好奇。其中很多产品穿越多个时间周期,期间不断创造内生二次曲线。</p>\n\n<p>但是阿里也一样错失了很多,微信、美团、拼多多、抖/快…… 等等很多产品诞生在了其他公司,还有某些产品在不断的科技更迭中自身生长出了第二曲线。</p>\n\n<p>因此我来阿里的梦想也非常明确:<strong>参与或创造一次(甚至多次)第二曲线,可以是新产品,也可以是原有产品内生的。在这个过程中获得个人成长、个人价值。</strong></p>\n\n<p>一直以来,我有三个最想实现或得到的东西:LOVE、CREATION、FREEDOM。随着生活与工作的前行,对这三者的理解,在不断加深。在这个问题里,我想应该是讨论”CREATION”。</p>\n\n<p>CREATION 上,我的梦想的范式,大概是从自己中学时代就确立了,在某一次人类社会变革浪潮中,扮演有一定权重的角色。这里面有几个变量:<strong>什么领域(F)的变革;什么规模(S)的变革;多大的权重(W);什么角色(R)。</strong></p>\n\n<p>F 这个变量,我在中学及大学时代逐渐明确,是以相对普适的产品形式输出结果并对社会变革产生积极作用。后来越来越明确为科技与商业结合的领域。</p>\n\n<p>S、W 这两个变量,自然是越大越好。因此我会希望能够构建尽可能大的机会,或者参与到尽可能大的机会中。R 希望是有强烈 Ownership 的身份。</p>\n\n<p>因此过去几年我选择了创业。创业就像冲浪,你抓住一次浪并完成漂亮的动作,就是一次不算失败的创业。但是如果一个浪没抓住,你去追它是没意义的,而应该等待下一个浪。我认为在未来 5~10 年内难以出现规模能大到令我足够兴奋的科技浪潮。大浪潮中属于创业者的大机会很多,而中小浪潮的大机会基本只属于大平台,那么为了在壮年期做获得我的 CREATION,我选择了加入阿里这样的大平台。</p>\n\n<p>在最后做决定以及初来阿里的那个人生转折点,作为老阿里人的曲洋老师对我说的一句话,深深地鼓励了我,他说:”带着创业气质,把这里当你的舞台折腾!”</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-2.png\" alt=\"image\" /></p>\n\n<p>注:2020 年双十一 · 淘宝 KO</p>\n\n<h3 id=\"最喜欢新六脉的哪句话为什么\">最喜欢新六脉的哪句话?为什么?</h3>\n\n<p>最喜欢的是“因为信任所以简单”。</p>\n\n<p>我一直认为人最重要的两个元特质是”真实”和“谦逊”,由”真实”可以塑造自我(对内)、构建信任(对外),后者可以带来清晰的边界,继而实现人与人之间高效的互动(这种互动包括各种人际关系在内,如婚姻、合伙、共事、合作等等)。</p>\n\n<p><strong>事物(虚实皆可)呈现在人的认知中,会得到三方面的投影:facts、opinion、feeling。如果我们足够真实,当我们需要把这三方面呈现给他人时,双方就能顺畅建立信任。信任的结果,就对应到这三方面:彼此之间建立共识(facts)、求同存异(opinion)、尊重感受(feeling),这就是”简单”。</strong></p>\n\n<p>另外一句是鼓励自己勇于绽放的一条:「此次此刻,非我莫属」。</p>\n\n<p>激情、自信、积极…… 通常行为统一表现为“勇于绽放自己”,绽放有表达(语言)与投身(行为)两种表现形式。更进一步推进就是”此次此刻,非我莫属”的阿里价值观。</p>\n\n<p>低调、稳重、谦逊,其实与“此次此刻,非我莫属“,并不矛盾。这点是我来到阿里后,发现自己在过去这些年的创业中已经不知不觉改变了,从 Introvert 逐渐变成了 Extrovert 的人,而且从曾经 social 中消耗能量,逐渐变为我现在可以感知到获得能量。这种变化,是我最近来阿里才确认发生的,此前因为自己创业者的身份没有察觉这种变化的发生。</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-3.png\" alt=\"image\" /></p>\n\n<p>注:2021 年秋 · 径山之行</p>\n\n<h3 id=\"关于阿里企业价值观为什么要接受这套价值观\">关于阿里企业价值观:为什么要接受这套价值观?</h3>\n\n<p>马老师和老逍都提到这个:我们是寻找同路人,而不是教育别人。这其实非常明晰地解释了为什么阿里要构建一个毛细血管网络一样的政委体系。基于这种用人理念,政委体系不敢说是最优解,但一定是优解(而且是否有更优解的论证没有意义)。</p>\n\n<p>对于个人,我的理解是要做两件事:<strong>1)构建自己的价值观体系(初始化);2)寻找价值观契合的公司(做匹配)。这两点里,没有任何地方提到”你要改变价值观为了契合你所在的公司”。</strong></p>\n\n<p>而企业价值观呢,其实可以分两部分看待:普世价值观、独特价值观。前者因为是普世的,所以到了哪个公司这种价值观都对,这点老逍也提了,比如“客户第一”。后者是个性化的,但不存在孰高孰低,就像一个人内向还是外向,你不能说哪个是错的。</p>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-4.png\" alt=\"image\" /></p>\n\n<p>注:2021 年双十一 · 天天特卖团队</p>\n\n<h3 id=\"价值观的本质意义极度务实视角是什么\">价值观的本质意义(极度务实视角)是什么?</h3>\n\n<p><strong>在充分考虑价值观适配使命、愿景基础上,价值观本身的意义,在和风细雨时(即企业价值观与其他价值判断相 match 时),是看不到的。但在暴风骤雨时(即企业价值观与其他价值判断相冲突时),就能显示其实实在在的作用了。</strong>我认为包括三类,前两个是阿里整体视角,第三个是阿里内部:</p>\n\n<ul>\n <li>经济体内,阿里与其他生态位的冲突或损益关系,如曾经的美蘑口一役。</li>\n <li>经济体内,其他的生态位之间的冲突或损益关系,如曾经的十月围城。</li>\n <li>阿里人的行为价值判断,如最近的钉钉代考事件、过往的各类廉政事件。</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-5.png\" alt=\"image\" /></p>\n\n<p>注:2021 年冬 · 淘宝天猫合并前合影</p>\n\n<h3 id=\"landing的sop\">Landing 的 SOP</h3>\n\n<p>大家都说 landing 充满挑战,马老师其实给出了 landing 的 SOP 三部曲:<strong>一起打过仗、一起创过新、一起度过难。三个经历都 close 才算 smooth landing。</strong></p>\n\n<p>集团人才策略层面、HR 实操层面、Leader 层面、,对于新人 landing 能做到什么程度的保障,其实每个新人感受到的不尽相同:</p>\n\n<ul>\n <li>集团层面,始终是在构建更好的新人 landing 环境的,这符合自身价值,这能打下很好的底子。</li>\n <li>实操层面,包括面试阶段对候选人的价值观判断、预期管理,面试及入职后公司文化及人才体系的事实呈现、内化吸收和长期解惑。</li>\n <li>Leader 层面,这是新人体感最强烈的部分,也是最重要的部分。尽管拥抱变化,但首先 Leader 需要给出尽可能最全面的考虑,其次是对候选人的预期管理。好的 Leader 会给候选人提供合理的着陆点、多个降落伞、缓冲垫,完成 smooth landing。</li>\n</ul>\n\n<p><img src=\"/img/src/2020-06-11-captain-alibaba-6.png\" alt=\"image\" /></p>\n\n<p>注:2021 年夏 · 出差厦漳泉</p>\n\n<h3 id=\"问问自己来到阿里如果初期我可能需要做一点改变那会是什么\">问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</h3>\n\n<p>曾经个人的激情与动力,常来自于“增长”。常说<strong>高增长掩盖一切</strong>,所以未来在阿里如果不能如创业般快速获得反馈得到积极结果,并且大平台中必然要接受大量关联方共同参与项目而导致的效率降低,因此我要逐渐改变自己,重新适应这种环境下的激情与动力获得方式。</p>\n\n<p>另一方面,作为创业公司的负责人,工作中鲜有因为内部原因而无法推进的事情,但是扮演肩部或腰部角色时,需要接受头部决策的一定程度不可控,这是我需要作出的适应与改变。关于这一点,我在几个月前就已经在做预期管理和心态调整,我认为以创业者的强适应性,这可能并不会是问题,但是我习惯于保持谨慎的乐观来面对自己。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\n \t<meta name=\"description\" content=\"AIGC,MidJourney,Image2Text,文生图\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\t\t\n\t<time datetime=\"2022-11-30T15:12:03+00:00\" class=\"by-line\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\n\t<div class=\"content\">\n\t\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-1.png\" alt=\"image\" /></th>\n <th><img src=\"/img/src/2022-12-16-midjourney-first-test-2.png\" alt=\"image\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\n\n<p><img src=\"/img/src/2022-12-16-midjourney-first-test-3.png\" alt=\"image\" />\n(<em>注:MidJourney 官网</em>)</p>\n\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\n\n<p>相应的,对抗性的防御技术也会很快发展。</p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\n \t<meta name=\"description\" content=\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\t\t\n\t<time datetime=\"2022-12-11T15:59:57+00:00\" class=\"by-line\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\n\t<div class=\"content\">\n\t\t<p><img src=\"/img/src/2022-12-11-wechat-chatgpt-3.png\" alt=\"image\" /></p>\n\n<h3 id=\"写在前面\">写在前面</h3>\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\n\n<h3 id=\"stepbystep\">Step by step</h3>\n\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\n\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\n\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\n\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\n\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\n\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\n\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\n\n<div class=\"language-yaml highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"na\">chatGPTAccountPool</span><span class=\"pi\">:</span>\n <span class=\"pi\">-</span> <span class=\"na\">email</span><span class=\"pi\">:</span> <span class=\"s\"><your email></span>\n <span class=\"na\">password</span><span class=\"pi\">:</span> <span class=\"s\"><your password></span>\n<span class=\"c1\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\n<span class=\"na\">chatPrivateTiggerKeyword</span><span class=\"pi\">:</span> <span class=\"s2\">\"</span><span class=\"s\">\"</span>\n</code></pre></div></div>\n\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker pull holegots/wechat-chatgpt:latest。\n</code></pre></div></div>\n\n<p>第八步,启动 wechat-chatgpt:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker run <span class=\"nt\">-d</span> <span class=\"nt\">--name</span> wechat-chatgpt <span class=\"nt\">-v</span> <span class=\"si\">$(</span><span class=\"nb\">pwd</span><span class=\"si\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\n</code></pre></div></div>\n\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\"language-plaintext highlighter-rouge\">npm install && poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\n\n<table>\n <thead>\n <tr>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-1.png\" alt=\"image\" style=\"width:100%\" /></th>\n <th><img src=\"/img/src/2022-12-11-wechat-chatgpt-2.png\" alt=\"image\" style=\"width:100%\" /></th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td> </td>\n <td> </td>\n </tr>\n </tbody>\n</table>\n\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\n\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker logs <span class=\"nt\">-f</span> wechat-chatgpt\n</code></pre></div></div>\n\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\n\n<p>如果要停止运行,用如下命令:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>docker stop wechat-chatgpt\n</code></pre></div></div>\n\n<h3 id=\"参考\">参考</h3>\n\n<p>1、<a href=\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\n \t<meta name=\"description\" content=\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\t\t\n\t<time datetime=\"2022-12-17T15:08:01+00:00\" class=\"by-line\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\n\t<div class=\"content\">\n\t\t<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一关于-bert-的一些背景\" id=\"markdown-toc-一关于-bert-的一些背景\">一、关于 BERT 的一些背景</a></li>\n <li><a href=\"#二开始一个-bert-的动手小试验\" id=\"markdown-toc-二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</a> <ul>\n <li><a href=\"#1安装-anaconda-来为部署-bert-做环境准备\" id=\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\n <li><a href=\"#2安装-bert-所需要的各种依赖\" id=\"markdown-toc-2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</a></li>\n <li><a href=\"#3下载一个预训练pre-train过的-bert-模型\" id=\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\n <li><a href=\"#5启动-bert-服务端\" id=\"markdown-toc-5启动-bert-服务端\">5、启动 BERT 服务端</a></li>\n <li><a href=\"#6在-pycharm-中使用-conda-的环境\" id=\"markdown-toc-6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</a></li>\n <li><a href=\"#7编写程序实现-bert-客户端\" id=\"markdown-toc-7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</a></li>\n </ul>\n </li>\n <li><a href=\"#三bert-模型的优劣势及其原因\" id=\"markdown-toc-三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</a> <ul>\n <li><a href=\"#1bert-的优势是很明显的\" id=\"markdown-toc-1bert-的优势是很明显的\">1、BERT 的优势是很明显的</a> <ul>\n <li><a href=\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\" id=\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\n <li><a href=\"#12识别并专注于较重要的部分进行文本处理\" id=\"markdown-toc-12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\n <li><a href=\"#13快速构建针对具体任务的-nlp-系统\" id=\"markdown-toc-13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\n </ul>\n </li>\n <li><a href=\"#2bert-模型的劣势及其原因\" id=\"markdown-toc-2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</a> <ul>\n <li><a href=\"#21随机挖-mask-的完形填空题是有隐患的\" id=\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\n <li><a href=\"#22nsp-任务有必要吗\" id=\"markdown-toc-22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</a></li>\n <li><a href=\"#23针对两个或以上词组成的连续词的词义被丢失\" id=\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\n <li><a href=\"#24需要的算力高\" id=\"markdown-toc-24需要的算力高\">2.4、需要的算力高</a></li>\n <li><a href=\"#25需要的模型大\" id=\"markdown-toc-25需要的模型大\">2.5、需要的模型大</a></li>\n </ul>\n </li>\n </ul>\n </li>\n <li><a href=\"#四一些关于-bert-的问题\" id=\"markdown-toc-四一些关于-bert-的问题\">四、一些关于 BERT 的问题</a> <ul>\n <li><a href=\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\" id=\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\n <li><a href=\"#2为什么-bert-可以比-rnn-更好地并行化\" id=\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\n </ul>\n </li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一关于-bert-的一些背景\">一、关于 BERT 的一些背景</h3>\n\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\n\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\n\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\n\n<h3 id=\"二开始一个-bert-的动手小试验\">二、开始一个 BERT 的动手小试验</h3>\n\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\n\n<h4 id=\"1安装-anaconda-来为部署-bert-做环境准备\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\n\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\n\n<p>更新 conda 到最新版本:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda update <span class=\"nt\">-n</span> base conda\n</code></pre></div></div>\n\n<p>使用 Python 3.7 创建一个新的环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda create <span class=\"nt\">-n</span> py37 <span class=\"nv\">python</span><span class=\"o\">=</span>3.7\n</code></pre></div></div>\n\n<p>激活这个新环境:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda activate py37\n</code></pre></div></div>\n\n<p>验证正在使用的是正确版本的 Python</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>python <span class=\"nt\">--version</span>\n</code></pre></div></div>\n\n<p>另外你可能还会用到的 conda 命令有:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\nconda deactivate py37\n\n<span class=\"c\"># 查看 conda 当前安装的所有库</span>\nconda list\n</code></pre></div></div>\n\n<h4 id=\"2安装-bert-所需要的各种依赖\">2、安装 BERT 所需要的各种依赖</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>conda <span class=\"nb\">install </span><span class=\"nv\">tensorflow</span><span class=\"o\">==</span>1.14.0\n</code></pre></div></div>\n\n<p>验证 tensorflow 是否安装正确:</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">import</span> <span class=\"nn\">tensorflow</span> <span class=\"k\">as</span> <span class=\"n\">tf</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">tf</span><span class=\"p\">.</span><span class=\"n\">__version__</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<h4 id=\"3下载一个预训练pre-train过的-bert-模型\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\n\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\n\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\n\n<ul>\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\n</ul>\n\n<p>4、安装 BERT 的服务端和客户端</p>\n\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\n\n<p>在你激活的环境里,安装 <code class=\"language-plaintext highlighter-rouge\">bert-as-service</code>:</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 安装服务端和客户端</span>\n<span class=\"c\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\nconda <span class=\"nb\">install </span>bert-serving-server bert-serving-client \n验证 bert-as-service 是否安装成功\nbert-serving-start <span class=\"nt\">-h</span>\n</code></pre></div></div>\n\n<h4 id=\"5启动-bert-服务端\">5、启动 BERT 服务端</h4>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"c\"># 命令行下启动BERT服务</span>\n<span class=\"c\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\nbert-serving-start <span class=\"nt\">-model_dir</span> /模型/的/绝对/路径 <span class=\"nt\">-num_worker</span><span class=\"o\">=</span>4\n</code></pre></div></div>\n\n<h4 id=\"6在-pycharm-中使用-conda-的环境\">6、在 PyCharm 中使用 Conda 的环境</h4>\n\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\n\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\"language-plaintext highlighter-rouge\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\n\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\n\n<h4 id=\"7编写程序实现-bert-客户端\">7、编写程序实现 BERT 客户端</h4>\n\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\n\n<div class=\"language-python highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"kn\">from</span> <span class=\"nn\">bert_serving.client</span> <span class=\"kn\">import</span> <span class=\"n\">BertClient</span>\n<span class=\"kn\">import</span> <span class=\"nn\">numpy</span> <span class=\"k\">as</span> <span class=\"n\">np</span>\n\n<span class=\"c1\"># 定义类\n</span><span class=\"k\">class</span> <span class=\"nc\">BertModel</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"nf\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span> <span class=\"o\">=</span> <span class=\"n\">BertClient</span><span class=\"p\">(</span><span class=\"n\">ip</span><span class=\"o\">=</span><span class=\"s\">'127.0.0.1'</span><span class=\"p\">,</span> <span class=\"n\">port</span><span class=\"o\">=</span><span class=\"mi\">5555</span><span class=\"p\">,</span> <span class=\"n\">port_out</span><span class=\"o\">=</span><span class=\"mi\">5556</span><span class=\"p\">)</span> <span class=\"c1\"># 创建客户端对象\n</span> <span class=\"c1\"># 注意:可以参考API,查看其它参数的设置\n</span> <span class=\"c1\"># 127.0.0.1 表示本机IP,也可以用localhost\n</span> <span class=\"k\">except</span><span class=\"p\">:</span>\n <span class=\"k\">raise</span> <span class=\"nb\">Exception</span><span class=\"p\">(</span><span class=\"s\">\"cannot create BertClient\"</span><span class=\"p\">)</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">close_bert</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">close</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">sentence_embedding</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">text</span><span class=\"p\">):</span>\n <span class=\"s\">'''对输入文本进行embedding\n Args:\n text: str, 输入文本\n Returns:\n text_vector: float, 返回一个列表,包含text的embedding编码值\n '''</span>\n <span class=\"n\">text_vector</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"p\">.</span><span class=\"n\">bert_client</span><span class=\"p\">.</span><span class=\"n\">encode</span><span class=\"p\">([</span><span class=\"n\">text</span><span class=\"p\">])[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"k\">return</span> <span class=\"n\">text_vector</span> <span class=\"c1\"># 获取输出结果\n</span>\n <span class=\"k\">def</span> <span class=\"nf\">caculate_similarity</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">vec_1</span><span class=\"p\">,</span> <span class=\"n\">vec_2</span><span class=\"p\">):</span>\n <span class=\"s\">'''根据两个语句的vector,计算它们的相似性\n Args:\n vec_1: float, 语句1的vector\n vec_2: float, 语句2的vector\n Returns:\n sim_value: float, 返回相似性的计算值\n '''</span>\n <span class=\"c1\"># 根据cosine的计算公式\n</span> <span class=\"n\">v1</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_1</span><span class=\"p\">)</span>\n <span class=\"n\">v2</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">mat</span><span class=\"p\">(</span><span class=\"n\">vec_2</span><span class=\"p\">)</span>\n <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">v1</span> <span class=\"o\">*</span> <span class=\"n\">v2</span><span class=\"p\">.</span><span class=\"n\">T</span><span class=\"p\">)</span>\n <span class=\"n\">b</span> <span class=\"o\">=</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v1</span><span class=\"p\">)</span> <span class=\"o\">*</span> <span class=\"n\">np</span><span class=\"p\">.</span><span class=\"n\">linalg</span><span class=\"p\">.</span><span class=\"n\">norm</span><span class=\"p\">(</span><span class=\"n\">v2</span><span class=\"p\">)</span>\n <span class=\"n\">cosine</span> <span class=\"o\">=</span> <span class=\"n\">a</span> <span class=\"o\">/</span> <span class=\"n\">b</span>\n <span class=\"k\">return</span> <span class=\"n\">cosine</span>\n\n\n<span class=\"k\">if</span> <span class=\"n\">__name__</span> <span class=\"o\">==</span> <span class=\"s\">\"__main__\"</span><span class=\"p\">:</span>\n <span class=\"c1\"># 创建bert对象\n</span> <span class=\"n\">bert</span> <span class=\"o\">=</span> <span class=\"n\">BertModel</span><span class=\"p\">()</span>\n <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n <span class=\"c1\"># --- 输入语句 ----\n</span> <span class=\"n\">input_a</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句1: '</span><span class=\"p\">)</span>\n\n <span class=\"k\">if</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"N\"</span> <span class=\"ow\">or</span> <span class=\"n\">input_a</span> <span class=\"o\">==</span> <span class=\"s\">\"n\"</span><span class=\"p\">:</span>\n <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">close_bert</span><span class=\"p\">()</span> <span class=\"c1\"># 关闭服务\n</span> <span class=\"k\">break</span>\n\n <span class=\"n\">input_b</span> <span class=\"o\">=</span> <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s\">'请输入语句2: '</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># --- 对输入语句进行embedding ---\n</span>\n <span class=\"n\">a_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_a</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'a_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">a_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"n\">b_vec</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">sentence_embedding</span><span class=\"p\">(</span><span class=\"n\">input_b</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'b_vec shape : '</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">.</span><span class=\"n\">shape</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 计算两个语句的相似性\n</span> <span class=\"n\">cos</span> <span class=\"o\">=</span> <span class=\"n\">bert</span><span class=\"p\">.</span><span class=\"n\">caculate_similarity</span><span class=\"p\">(</span><span class=\"n\">a_vec</span><span class=\"p\">,</span> <span class=\"n\">b_vec</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'cosine value : '</span><span class=\"p\">,</span> <span class=\"n\">cos</span><span class=\"p\">)</span>\n\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">'</span><span class=\"se\">\\n\\n</span><span class=\"s\">'</span><span class=\"p\">)</span>\n\n <span class=\"c1\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\n</span> <span class=\"k\">if</span> <span class=\"n\">cos</span> <span class=\"o\">></span> <span class=\"mf\">0.85</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"2个语句的含义相似\"</span><span class=\"p\">)</span>\n <span class=\"k\">else</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">\"不相似\"</span><span class=\"p\">)</span>\n</code></pre></div></div>\n\n<p>在使用 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 连接 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 时,你需要确保 <code class=\"language-plaintext highlighter-rouge\">bert-serving-server</code> 使用的模型和 <code class=\"language-plaintext highlighter-rouge\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\n\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\n\n<div class=\"language-shell highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>请输入语句1: \n请输入语句2: \n</code></pre></div></div>\n\n<p>两句输入好确认后,得到如下形式的结果:</p>\n\n<div class=\"language-plaintext highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code>a_vec shape : (768,)\nb_vec shape : (768,)\ncosine value : 0.8691698561422959\n</code></pre></div></div>\n\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\n\n<h3 id=\"三bert-模型的优劣势及其原因\">三、BERT 模型的优劣势及其原因</h3>\n\n<p>论文地址:<a href=\"https://arxiv.org/abs/1810.04805\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\n\n<h4 id=\"1bert-的优势是很明显的\">1、BERT 的优势是很明显的</h4>\n\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\n\n<blockquote>\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\n</blockquote>\n\n<h5 id=\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\n\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\n\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\n\n<h5 id=\"12识别并专注于较重要的部分进行文本处理\">1.2、识别并专注于较重要的部分进行文本处理</h5>\n\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\n\n<h5 id=\"13快速构建针对具体任务的-nlp-系统\">1.3、快速构建针对具体任务的 NLP 系统</h5>\n\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\n\n<h4 id=\"2bert-模型的劣势及其原因\">2、BERT 模型的劣势及其原因</h4>\n\n<h5 id=\"21随机挖-mask-的完形填空题是有隐患的\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\n\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\n\n<h5 id=\"22nsp-任务有必要吗\">2.2、NSP 任务有必要吗?</h5>\n\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\n\n<h5 id=\"23针对两个或以上词组成的连续词的词义被丢失\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\n\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\n\n<h5 id=\"24需要的算力高\">2.4、需要的算力高</h5>\n\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\n\n<h5 id=\"25需要的模型大\">2.5、需要的模型大</h5>\n\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\n\n<h3 id=\"四一些关于-bert-的问题\">四、一些关于 BERT 的问题</h3>\n\n<h4 id=\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\n\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\n\n<h4 id=\"2为什么-bert-可以比-rnn-更好地并行化\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\n\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://arxiv.org/abs/1810.04805</li>\n <li>https://github.com/google-research/bert</li>\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n","<!DOCTYPE html>\n<html>\n\n<head>\n\t<!-- Meta -->\n\t<meta charset=\"UTF-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1\">\n\t<meta name=\"generator\" content=\"Jekyll\">\n\n\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\n \t<meta name=\"description\" content=\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\">\n\n\t<!-- CSS & fonts -->\n\t<link rel=\"stylesheet\" href=\"/pages/Poechant/css/main.css\">\n\n\t<!-- RSS -->\n\t<link href=\"/atom.xml\" type=\"application/atom+xml\" rel=\"alternate\" title=\"ATOM Feed\" />\n\n \t<!-- Favicon -->\n \t <link rel=\"shortcut icon\" type=\"image/png\" href=\"/img/favicon.png\">\n\n \t <!-- Syntax highlighter -->\n \t<link rel=\"stylesheet\" href=\"/css/syntax.css\" />\n\n \t<!--KaTeX-->\n \t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\" integrity=\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\" crossorigin=\"anonymous\"></script>\n \t<script defer src=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\" integrity=\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\" crossorigin=\"anonymous\"></script>\n \t<script>\n \t\tdocument.addEventListener(\"DOMContentLoaded\", function() {\n \t\t\trenderMathInElement(document.body, {\n \t\t\t\t// ...options...\n \t\t\t});\n \t\t});\n \t</script>\n\n \t\n\n</head>\n\n<body>\n\t<div id=\"wrap\">\n\t \t\n\t \t<!-- Navigation -->\n\t \t<nav id=\"nav\">\n\t<div id=\"nav-list\">\n\t\t<a href=\"/pages/Poechant/\">Home</a>\n\n\t\t<!-- Nav pages -->\n\t <!-- \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n\t \n\t \n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n\t \n\t \n\t \n\t <a href=\"/pages/Poechant/categories/\" title=\"Categories\">Categories</a>\n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t \n\t -->\n\n\t <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n\t <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n\t</div>\n \n <!-- Nav footer -->\n\t\n\t <footer>\n\t\n\t<span>version 1.0.0</span>\n\n</footer>\n\t\n\n</nav>\n\n \n <!-- Icon menu -->\n\t <a id=\"nav-menu\">\n\t \t<div id=\"menu\"></div>\n\t </a>\n\n <!-- Header -->\n \n <header id=\"header\" class=\"parent justify-spaceBetween\">\n <div class=\"inner w100 relative\">\n <span class=\"f-left\"> \n <a href=\"/pages/Poechant/\">\n <h1>\n <span>Mike</span>Captain\n </h1>\n </a>\n </span>\n <span id=\"nav-links\" class=\"absolute right bottom\">\n\n <!-- Tech category pages -->\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/ai\" title=\"人工智能\">人工智能</a>\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/rt_tech\" title=\"实时技术\">实时技术</a>\n\n\n\n\n\n <a href=\"/pages/Poechant/category/web\" title=\"前端技术\">前端技术</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!-- Non-tech category pages -->\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n <a href=\"/pages/Poechant/category/thinking\" title=\"思考与生活\">思考与生活</a>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n 丨 \n\n <!-- Nav pages -->\n \n \n \n \n <a href=\"/pages/Poechant/about/\" title=\"关于我\">关于我</a>\n \n \n \n \n \n <a href=\"/pages/Poechant/booklist/\" title=\"我的书单\">我的书单</a>\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <!-- Nav links -->\n <!-- <a href=\"https://github.com/thereviewindex/monochrome/archive/master.zip\">Download</a>\n<a href=\"https://github.com/thereviewindex/monochrome\">Project on Github</a> -->\n\n </span>\n </div>\n</header>\n\n\n\n\n \n\n <!-- Main content -->\n\t <div id=\"container\">\n\t\t \n\t\t<main>\n\n\t\t\t<article id=\"post-page\">\n\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\t\t\n\t<time datetime=\"2022-12-24T15:08:01+00:00\" class=\"by-line\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\n\t<div class=\"content\">\n\t\t<ul>\n <li>作者:麦克船长(钟超)</li>\n <li>微信:sinosuperman</li>\n</ul>\n\n<p><strong>本文目录</strong></p>\n<ul id=\"markdown-toc\">\n <li><a href=\"#一自然语言处理领域近年的发展关键节点\" id=\"markdown-toc-一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</a> <ul>\n <li><a href=\"#1从理性主义到经验主义\" id=\"markdown-toc-1从理性主义到经验主义\">1、从理性主义到经验主义</a></li>\n <li><a href=\"#2经验主义的早期还不是深度学习\" id=\"markdown-toc-2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</a></li>\n <li><a href=\"#3撇开特征让机器囫囵吞枣地学吧\" id=\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\n <li><a href=\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\" id=\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\n <li><a href=\"#5自监督学习法让我们省去人工标注\" id=\"markdown-toc-5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</a></li>\n <li><a href=\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\" id=\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\n <li><a href=\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\" id=\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\n <li><a href=\"#8数据和算力有了还不够\" id=\"markdown-toc-8数据和算力有了还不够\">8、数据和算力有了,还不够</a></li>\n </ul>\n </li>\n <li><a href=\"#二学术里程碑几篇重量级论文\" id=\"markdown-toc-二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</a> <ul>\n <li><a href=\"#1提出-transformer-的attention-is-all-you-need2017\" id=\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\n <li><a href=\"#2elmo-deep-contextualized-word-representations\" id=\"markdown-toc-2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</a></li>\n <li><a href=\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\" id=\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\n <li><a href=\"#4gpt-3-language-models-are-few-shot-learners2020\" id=\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\n <li><a href=\"#其他的重量级论文\" id=\"markdown-toc-其他的重量级论文\">其他的重量级论文</a></li>\n </ul>\n </li>\n <li><a href=\"#三行业里程碑\" id=\"markdown-toc-三行业里程碑\">三、行业里程碑</a></li>\n <li><a href=\"#四成本\" id=\"markdown-toc-四成本\">四、成本</a></li>\n <li><a href=\"#五业内应用\" id=\"markdown-toc-五业内应用\">五、业内应用</a></li>\n <li><a href=\"#五行业内哪些人的言论值得我们日常重点关注\" id=\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\n <li><a href=\"#reference\" id=\"markdown-toc-reference\">Reference</a></li>\n</ul>\n\n<h3 id=\"一自然语言处理领域近年的发展关键节点\">一、自然语言处理领域近年的发展关键节点</h3>\n\n<p><img src=\"/img/src/2022-12-17-ai-bert-1-1.jpg\" alt=\"image\" /></p>\n\n<h4 id=\"1从理性主义到经验主义\">1、从理性主义到经验主义</h4>\n\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\n\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\n\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\n\n<h4 id=\"2经验主义的早期还不是深度学习\">2、经验主义的早期,还不是深度学习</h4>\n\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\n\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\n\n<h4 id=\"3撇开特征让机器囫囵吞枣地学吧\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\n\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\n\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\n\n<h4 id=\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\n\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\n\n<h4 id=\"5自监督学习法让我们省去人工标注\">5、自监督学习法,让我们省去人工标注</h4>\n\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\n\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\n\n<h4 id=\"6用原始的任务训练出来的模型能迁移去解决新任务吗\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\n\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\n\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\n\n<h4 id=\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\n\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\n\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\n\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\n\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\n\n<h4 id=\"8数据和算力有了还不够\">8、数据和算力有了,还不够</h4>\n\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\n\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\n\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\n\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\n\n<h3 id=\"二学术里程碑几篇重量级论文\">二、学术里程碑:几篇重量级论文</h3>\n\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\n\n<h4 id=\"1提出-transformer-的attention-is-all-you-need2017\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\n\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\n\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\n\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\n\n<h4 id=\"2elmo-deep-contextualized-word-representations\">2、ELMo: Deep contextualized word representations</h4>\n\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\n\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\n\n<h4 id=\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\n\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\n\n<ul>\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\n <li>在预训练中引入自监督学习任务。</li>\n</ul>\n\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\n\n<h4 id=\"4gpt-3-language-models-are-few-shot-learners2020\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\n\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\n\n<h4 id=\"其他的重量级论文\">其他的重量级论文</h4>\n\n<ul>\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\n <li>……</li>\n</ul>\n\n<h3 id=\"三行业里程碑\">三、行业里程碑</h3>\n\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\n\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\n\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\n\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\n\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\n\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\n\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\n\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\n\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\n\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\n\n<h3 id=\"四成本\">四、成本</h3>\n\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\n\n<ul>\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\n</ul>\n\n<h3 id=\"五业内应用\">五、业内应用</h3>\n\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\n\n<ul>\n <li>虚拟客服(可以乱真的)</li>\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\n <li>智能翻译</li>\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\n <li>AI 广告公司:替代传统广告公司</li>\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\n</ul>\n\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\n\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\n\n<ul>\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-7.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-2.jpg\" alt=\"image\" /></p>\n\n<ul>\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-8.png\" alt=\"image\" /></p>\n\n<ul>\n <li>2021 年年底,西湖心辰公司发布「<a href=\"https://www.heyfriday.cn/\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\n</ul>\n\n<p><img src=\"/img/src/2022-12-24-captain-nlp-1.png\" alt=\"image\" /></p>\n\n<h3 id=\"五行业内哪些人的言论值得我们日常重点关注\">五、行业内哪些人的言论值得我们日常重点关注</h3>\n\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\n\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\n\n<blockquote>\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\n</blockquote>\n\n<blockquote>\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\n</blockquote>\n\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\n\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\n\n<blockquote>\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\n</blockquote>\n\n<blockquote>\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\n</blockquote>\n\n<h3 id=\"reference\">Reference</h3>\n\n<ol>\n <li>https://beta.openai.com/docs/models</li>\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\n <li>https://hub.baai.ac.cn/view/21726</li>\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\n <li>https://www.sohu.com/a/615541698_121255906</li>\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\n</ol>\n\n\t</div>\n</article>\n\n\n\n\t </main>\n\t\t\n\t\t <!-- Pagination links -->\n \n\n\t </div>\n\t \n\t <!-- Footer -->\n\t <footer><span>@2022 - MikeCaptain.com</span></footer>\n\n\n\t <!-- Script -->\n <script src=\"/pages/Poechant/js/main.js\"></script>\t\n\n\n\t</div>\n</body>\n</html>\n"],"output":true,"label":"posts","directory":"/home/runner/work/poechant.github.io/poechant.github.io/_posts","files":[],"permalink":"/:year/:month/:day/:title/"}],"html_pages":["<h1 id=\"404---page-not-found\">404 - Page not found</h1>\n<p>Sorry, we couldn’t find the requested URL. You can try again by going <a href=\"/pages/Poechant\">back to the homepage</a>.</p>\n","<p><img src=\"/pages/Poechant/img/about/avatar.jpg\" alt=\"image\" /></p>\n\n<h3 id=\"麦克船长\">麦克船长</h3>\n\n<p>本名钟超,现任阿里巴巴集团大聚划算运营中心负责人,曾先后负责淘宝行业产品团队、天天特卖等。关注人工智能、电子商务、社交网络、数字人领域。2012 年被评选为 CSDN 中国十大技术博客(CSDN ID:Poechant)。</p>\n\n<h4 id=\"工作经历\">工作经历</h4>\n\n<ul>\n <li>2020 至今,阿里巴巴集团,<strong>总监/资深综合运营专家(P9)</strong></li>\n <li>2014 - 2020,七海互联(Qihai Inc.),<strong>CEO & CTO</strong></li>\n <li>2012 - 2014,领拓云合(LightOnUs),<strong>联合创始人 & 产品总裁</strong></li>\n <li>2011 - 2012,欢聚集团(纳斯达克上市公司,股票代号 YY),<strong>Web YY 音视频技术负责人</strong></li>\n</ul>\n\n<h4 id=\"我的学业\">我的学业</h4>\n\n<ul>\n <li>2011 年,中国科学技术大学,计算机系本科毕业。</li>\n</ul>\n\n<h4 id=\"我的社交媒体\">我的社交媒体</h4>\n\n<ul>\n <li>微信:<strong>sinosuperman</strong>(添加请注明「学校/单位/公司」、「职位」和「来意」才能通过,多谢理解!)</li>\n <li>Bilibili:<a href=\"https://space.bilibili.com/482553760\" target=\"_blank\"><u>船长模玩</u></a> —— 打小喜欢变形金刚,成年后补偿性消费收藏变形金刚手办😂</li>\n <li>新浪微博:<a href=\"http://weibo.com/lauginhom\" target=\"_blank\"><u>@船长还不会游泳</u></a></li>\n <li>知乎:<a href=\"https://www.zhihu.com/people/poechant\" target=\"_blank\"><u>船长还不会游泳</u></a></li>\n</ul>\n\n<h4 id=\"我的爱好\">我的爱好</h4>\n\n<ul>\n <li>滑雪、拳击</li>\n <li>电影:豆瓣标记「看过」的影视作品 800 部左右,与数千部的大佬仍有差距(略略略)</li>\n <li>我的书单:<a href=\"/booklist/\">麦克船长的书单</a></li>\n <li>变形金刚手办收藏</li>\n</ul>\n\n<p><img src=\"/pages/Poechant/img/about/photo_10.jpg\" alt=\"image\" />\n<img src=\"/pages/Poechant/img/about/photo_2.jpg\" alt=\"image\" />\n<img src=\"/pages/Poechant/img/about/photo_6.jpg\" alt=\"image\" />\n<img src=\"/pages/Poechant/img/about/photo_3.jpg\" alt=\"image\" />\n<img src=\"/pages/Poechant/img/about/photo_4.jpg\" alt=\"image\" />\n<img src=\"/pages/Poechant/img/about/photo_5.jpg\" alt=\"image\" />\n<img src=\"/pages/Poechant/img/about/photo_1.jpg\" alt=\"image\" />\n<img src=\"/img/about/photo_7.jpg\" alt=\"image\" />\n<img src=\"/img/about/photo_8.jpg\" alt=\"image\" />\n<img src=\"/pages/Poechant/img/about/photo_9.jpg\" alt=\"image\" /></p>\n","","<p>麦克船长是谁?<a href=\"/about/\">这里有个简单的介绍方便了解我</a>。</p>\n\n<h3 id=\"麦克船长的-2022-2023-书单\">麦克船长的 2022-2023 书单</h3>\n\n<p>重点关注:1)人工智能的技术、商业书籍;2)组织行为学;3)电商、零售与供应链管理;4)互联网产品、商业模式;5)Crypto 及 Web3;6)经济学、策略及博弈。</p>\n\n<ul>\n <li>《自然语言处理:基于预训练模型的方法》车万翔</li>\n <li>《自然语言处理实战:预训练模型应用及其产品化》安库·A·帕特尔</li>\n <li>《PyTorch 深度学习实战(第 2 版)》弗朗索瓦·肖莱</li>\n <li>《Python 机器学习经典实例》普拉提克·乔西</li>\n <li>《深度学习》伊恩·古德费洛</li>\n <li>《AI 未来进行式》李开复</li>\n <li>《AI 3.0》梅拉妮·米歇尔</li>\n <li>《从 AIoT 到元宇宙:关键技术、产业图景与未来展望》</li>\n <li>《科学之路》杨立昆</li>\n <li>《虚拟人》玛蒂娜·罗斯布拉特</li>\n <li>《给大忙人的高效阅读课》</li>\n <li>《西线无战事》</li>\n <li>《纳瓦尔宝典》</li>\n <li>《刀锋》</li>\n <li>《策略思维:商界、政界及日常生活中的策略竞争》</li>\n <li>《CEO 管理与组织行为学》尤建新</li>\n <li>《组织行为学》孙晓岭,中国人民大学出版社</li>\n <li>《纳瓦尔宝典》</li>\n <li>《美国四百年:冒险、创新与财富塑造的历史》</li>\n <li>《历史的温度 1-5 》</li>\n <li>《自我边界》</li>\n <li>《以幽默的方式过一生》琢磨先生</li>\n <li>《半小时漫画经济学金融危机合集》</li>\n <li>《权力:为什么只有某些人所拥有》杰弗瑞·菲佛</li>\n <li>《制造消费者:消费主义全球史》</li>\n <li>《区块链启示录》</li>\n</ul>\n","<div id=\"archives\">\n\n <div class=\"archive-group\">\n \n <div id=\"#rt_tech\"></div>\n <p></p>\n\n <h3 class=\"category-head\">rt_tech</h3>\n <a name=\"rt_tech\"></a>\n <ul>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/08/04/openrtmfp-cumulus-9/\">OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/07/23/openrtmfp-cumulus-8/\">OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/06/25/openrtmfp-cumulus-7/\">OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/06/07/openrtmfp-cumulus-6/\">OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/04/24/openrtmfp-cumulus-5/\">OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/04/24/openrtmfp-cumulus-4/\">OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/04/15/openrtmfp-cumulus-3/\">OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/04/14/openrtmfp-cumulus-2/\">OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/04/09/openrtmfp-cumulus-1/\">OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</a></h4>\n </article>\n </li>\n \n </ul>\n </div>\n\n <div class=\"archive-group\">\n \n <div id=\"#thinking\"></div>\n <p></p>\n\n <h3 class=\"category-head\">thinking</h3>\n <a name=\"thinking\"></a>\n <ul>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2022/08/11/captain-alibaba/\">不要船开远了,就忘了为什么启航</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2021/11/11/captain-tttm/\">欢迎成为「淘宝-天天特卖」团队的创业合伙人!</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2021/06/04/captain-alibaba-1st-anniversary/\">麦克船长的阿里一年香(入职阿里一周年)</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2020/11/11/captain-double-eleven/\">担任淘宝产品总负责人的双十一,是怎样的体验?</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2020/04/14/covid2019-catering-business-mode/\">疫后怎么做餐饮品牌?三叉戟模式或成标配</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2020/04/11/delayed-gratification/\">延迟满足,才有自由</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2017/02/23/ai-make-people-life-as-billionaires/\">未来人工智能就是要:让普通人过上现在富豪们的生活</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2017/01/31/danshari-vs-remember-reverberations/\">我们是应该「断舍离」还是「念念不忘,必有回响」</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2012/05/15/excellent-software-engineer-basic-skills/\">一名出色软件工程师的技术基本功:编程与工具</a></h4>\n </article>\n </li>\n \n </ul>\n </div>\n\n <div class=\"archive-group\">\n \n <div id=\"#web\"></div>\n <p></p>\n\n <h3 class=\"category-head\">web</h3>\n <a name=\"web\"></a>\n <ul>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2021/12/23/captains-jeckyll-learning/\">麦克船长的 Jekyll 快速教程</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2021/12/21/build-github-pages-with-jekyll/\">如何使用 Jekyll 基于 Github Pages 搭建个人博客</a></h4>\n </article>\n </li>\n \n </ul>\n </div>\n\n <div class=\"archive-group\">\n \n <div id=\"#ai\"></div>\n <p></p>\n\n <h3 class=\"category-head\">ai</h3>\n <a name=\"ai\"></a>\n <ul>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2022/12/24/captain-nlp-1/\">自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2022/12/17/ai-bert-1/\">你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2022/12/11/wechat-chatgpt/\">动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</a></h4>\n </article>\n </li>\n \n <li>\n <article class=\"archive-item\">\n <h4><a href=\"/pages/Poechant/2022/11/30/midjourney-first-test/\">确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</a></h4>\n </article>\n </li>\n \n </ul>\n </div>\n\n</div>","<!-- <pre width=\"100%\">\nsite: {"config":null,"documents":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</title>\\n \\t<meta name=\\"description\\" content=\\"RTMFP 是 Adobe 开发的基于 UDP 协议的实时传输媒体流协议,支持 P2P 传输,具有较高的实时性和安全性。它的主要应用场景是视频通信、语音通信和网络游戏。OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。Cumulus 是一个基于 OpenRTMFP 的服务器,提供 RTMFP 服务。它具有轻量级、跨平台和可扩展的特点,并且还提供了负载均衡和可扩展性解决方案。YY 语音的 Web 端音视频流媒体能力,正是基于 RTMFP 协议做的迭代优化实现的。本文是船长关于这个系列文章的第一篇。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-09T18:57:19+00:00\\" class=\\"by-line\\">09 Apr 2012, 广州 | 作者 麦克船长 | 总计 8401 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfp是什么\\" id=\\"markdown-toc-一rtmfp是什么\\">一、RTMFP 是什么?</a> <ul>\\n <li><a href=\\"#文件分享-p2p-和实时流媒体-p2p-的区别是什么\\" id=\\"markdown-toc-文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</a></li>\\n <li><a href=\\"#rtmfp-和-rtmp-之间的区别是什么\\" id=\\"markdown-toc-rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</a></li>\\n <li><a href=\\"#flash-player-支持-rtmfp-吗\\" id=\\"markdown-toc-flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</a></li>\\n <li><a href=\\"#cumulus-使用-adobe-的-cirrus-key-吗\\" id=\\"markdown-toc-cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</a></li>\\n <li><a href=\\"#这个开源项目合法吗\\" id=\\"markdown-toc-这个开源项目合法吗\\">这个开源项目合法吗?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二openrtmfp和cumulus\\" id=\\"markdown-toc-二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</a></li>\\n <li><a href=\\"#三入门介绍与部署cumulusserver\\" id=\\"markdown-toc-三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</a> <ul>\\n <li><a href=\\"#1背景介绍\\" id=\\"markdown-toc-1背景介绍\\">1、背景介绍</a></li>\\n <li><a href=\\"#2准备工作\\" id=\\"markdown-toc-2准备工作\\">2、准备工作</a></li>\\n <li><a href=\\"#3安装\\" id=\\"markdown-toc-3安装\\">3、安装</a> <ul>\\n <li><a href=\\"#31外部依赖的安装\\" id=\\"markdown-toc-31外部依赖的安装\\">3.1、外部依赖的安装</a></li>\\n <li><a href=\\"#32安装openrtmfpcumulus\\" id=\\"markdown-toc-32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#4配置\\" id=\\"markdown-toc-4配置\\">4、配置</a></li>\\n <li><a href=\\"#5启动\\" id=\\"markdown-toc-5启动\\">5、启动</a></li>\\n <li><a href=\\"#6基本使用\\" id=\\"markdown-toc-6基本使用\\">6、基本使用</a></li>\\n <li><a href=\\"#7扩展cumulusserverserverapplication\\" id=\\"markdown-toc-7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三用lua编写helloworld应用扩展cumulusserver\\" id=\\"markdown-toc-三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</a> <ul>\\n <li><a href=\\"#1server-side\\" id=\\"markdown-toc-1server-side\\">1、Server-side</a> <ul>\\n <li><a href=\\"#11serverconfiguration\\" id=\\"markdown-toc-11serverconfiguration\\">1.1、Server configuration</a></li>\\n <li><a href=\\"#12applicationfile\\" id=\\"markdown-toc-12applicationfile\\">1.2、Application file</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2client-side\\" id=\\"markdown-toc-2client-side\\">2、Client-side</a></li>\\n <li><a href=\\"#3运行结果\\" id=\\"markdown-toc-3运行结果\\">3、运行结果</a></li>\\n <li><a href=\\"#4远程测试一个免费的测试服务器\\" id=\\"markdown-toc-4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<h3 id=\\"一rtmfp是什么\\">一、RTMFP 是什么?</h3>\\n\\n<p>Real-Time Media Flow Protocol(RTMFP)是 Adobe 开发的一种基于 UDP 并支持 P2P 的实时传输媒体流。主要特点是:高传输效率(可以使用压缩和算法来优化流量从而提高传输效率)、高实时性(可以保证媒体流的实时性使得视频通信和其他实时通信更加流畅)、支持 P2P 传输(减少对服务器的依赖从而减少带宽和服务器资源消耗)、高安全性(加密媒体流从而保证其安全性)。</p>\\n\\n<p>RTMFP 的主要应用场景包括:视频通信(视频聊天和视频会议)、语音通信(语音聊天、电话)、网络游戏。不过 RTMFP 目前仅有 Adobe 开发的版本,所以它并不是个开源项目,而是个商业化服务。那么有没有开源版本呢?</p>\\n\\n<h4 id=\\"文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</h4>\\n\\n<p>RTMFP 是一个 P2P 系统,但它仅针对实时通信时直接用户到用户之间的通信而设计,不能用于多个对等方之间进行文件共享(使用分段下载)。Facebook 在其 Pipe 应用中使用此协议将大文件直接在两个用户之间传输。</p>\\n\\n<h4 id=\\"rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</h4>\\n\\n<p>RTMP 是实时消息协议,RTMFP 代表实时媒体流协议。RTMFP 基于用户数据报协议(UDP),而 RTMP 基于传输控制协议(TCP)。\\n与 RTMP 不同,RTMFP 还支持直接从一个 Adobe Flash Player 传输数据到另一个,而无需经过服务器。</p>\\n\\n<h4 id=\\"flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</h4>\\n\\n<p>RTMFP 是基于用户数据报协议(UDP)的,而 RTMP 是基于传输控制协议(TCP)的。与 RTMP 不同,RTMFP 还支持直接在两个 Adobe Flash Player 之间发送数据,而不经过服务器。Flash Player 10.0 仅允许一对一通信进行 P2P,但从 10.1 开始允许应用程序级别的多播。Flash Player 查找适当的分发路由(覆盖网络),并可以将其分发到通过 P2P 连接的组。</p>\\n\\n<h4 id=\\"cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</h4>\\n\\n<p>不!当然,这是Cumulus的主要目标:成为Cirrus GPL的替代品。唯一的限制是:你的CPU,内存和单台机器的端口数。</p>\\n\\n<h4 id=\\"这个开源项目合法吗\\">这个开源项目合法吗?</h4>\\n\\n<p>在美国,数字千年版权法(Digital Millennium Copyright Act)规定,逆向工程用于协议互操作性是合法的。你可以在 WikiPedia 上查看相关讨论:</p>\\n\\n<ul>\\n <li>http://en.wikipedia.org/wiki/Real_Time_Media_Flow_Protocol</li>\\n <li>http://en.wikipedia.org/wiki/Proprietary_protocol</li>\\n <li>http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act</li>\\n</ul>\\n\\n<p>当逆向工程的目的是协议互操作性时,有法律先例。在美国,数字千年版权法(Digital Millennium Copyright Act)为逆向工程软件以使其与其他软件互操作提供了安全保障。</p>\\n\\n<h3 id=\\"二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</h3>\\n\\n<p>OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。它包含了 RTMFP 协议的实现,以及一些额外的功能,如媒体流传输、P2P 通信、脚本引擎和数据存储。</p>\\n\\n<p>Cumulus 是一个基于 OpenRTMFP 的服务器,是一个完整的开源且跨平台的 RTMFP 服务器,可通过脚本进行扩展。CumulusServer 根据 GPL 许可在考虑以下 4 个概念的情况下开发:速度、轻量、跨平台和可扩展。尽管尚未发布版本,但只有在 CumulusServer 经过测试和批准后才会将代码推送到 github。实际上,主要稳定的功能有:</p>\\n\\n<ul>\\n <li>P2P rendez-vous service</li>\\n <li>live streaming</li>\\n <li>RPC、pull、push exchange,实际上客户端和服务器之间的所有 AMF 可能交换</li>\\n <li>脚本引擎,用于创建自己的应用服务器或扩展 Cumulus 的功能</li>\\n <li>可扩展性和负载均衡解决方案</li>\\n</ul>\\n\\n<p>下面的内容是本篇 blog 的重点,包括两部分:先是 OpenRTMFP 应用的核心 CumulusServer 的入门介绍与部署,然后用 Lua 编写 HelloWorld 应用扩展 CumulusServer,我们开始吧!</p>\\n\\n<h3 id=\\"三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</h3>\\n\\n<h4 id=\\"1背景介绍\\">1、背景介绍</h4>\\n\\n<p>OpenRTMFP 可以帮助你实现 Flash 的实时应用的高并发扩展,OpenRTMFP/Cumulus 是基于 GNU General Public License 的。</p>\\n\\n<p>POCO:POrtable COmponents,是一个强大的开源 C++ 库。其在 C++ 开发中的角色,相当于 Java Class Library、苹果的 Cocoa、.NET framework。</p>\\n\\n<h4 id=\\"2准备工作\\">2、准备工作</h4>\\n\\n<p>下载:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><strong>External Dependencies</strong></th>\\n <th><strong>Official Site</strong></th>\\n <th><strong>Windows</strong></th>\\n <th><strong>Linux/OSX</strong></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>OpenSSL</td>\\n <td><a href=\\"http://www.slproweb.com/products/Win32OpenSSL.html\\">Official Site</a></td>\\n <td><a href=\\"http://www.slproweb.com/download/Win32OpenSSL_Light-1_0_1.exe\\">Download</a></td>\\n <td><a href=\\"http://www.openssl.org/source/openssl-1.0.1.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>Lua</td>\\n <td><a href=\\"http://www.lua.org/\\">Official Site</a></td>\\n <td><a href=\\"http://luaforwindows.googlecode.com/files/LuaForWindows_v5.1.4-45.exe\\">Download</a></td>\\n <td><a href=\\"http://www.lua.org/ftp/lua-5.1.5.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>POCO</td>\\n <td><a href=\\"http://pocoproject.org/\\">Official Site</a></td>\\n <td><a href=\\"http://downloads.sourceforge.net/project/poco/sources/poco-1.4.3/poco-1.4.3p1.zip\\">Download</a></td>\\n <td><a href=\\"https://sourceforge.net/projects/poco/files/sources/poco-1.4.3/poco-1.4.3p1.tar.gz/download\\">Download</a></td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>注意:POCO for linux 版本必须是 1.4.0 或更高,否则会引起 TCP 相关的 bug。</p>\\n\\n<h4 id=\\"3安装\\">3、安装</h4>\\n\\n<h5 id=\\"31外部依赖的安装\\">3.1、外部依赖的安装</h5>\\n\\n<p>Windows 下略,Linux 下基本就是:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>./configuremakesudo\\n<span class=\\"nv\\">$ </span>make <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</h5>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>OpenRTMFP-Cumulus/CumulusLib\\n<span class=\\"nv\\">$ </span>make\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd</span> ../CumulusServer\\n<span class=\\"nv\\">$ </span>make\\n</code></pre></div></div>\\n\\n<p>如果出现了 <code class=\\"language-plaintext highlighter-rouge\\">.h</code> 文件、lib 库找不到的情况,请修改 OpenRTMFP-Cumulus/CumulusLib/Makefile 或 OpenRTMFP-Cumulus/CumulusServer/Makefile。</p>\\n\\n<h4 id=\\"4配置\\">4、配置</h4>\\n\\n<p>通过编写 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP-Cumulus/CumulusServer/CumulusServer.ini</code> 文件来为 OpenRTMFP-Cumulus 进行个性化配置(默认是没有这个文件的),这个文件的内容形如:</p>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1985</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span>\\n<span class=\\"n\\">name</span><span class=\\"o\\">=</span><span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span><span class=\\"o\\">=</span><span class=\\"n\\">C</span><span class=\\"p\\">:</span><span class=\\"o\\">/</span><span class=\\"n\\">CumulusServer</span><span class=\\"o\\">/</span><span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<p>一些字段的设置含义如下,摘自:<a href=\\"https://github.com/OpenRTMFP/Cumulus/wiki/Installation\\">地址</a>。</p>\\n\\n<ul>\\n <li>公开给 Client 的端口号 <code class=\\"language-plaintext highlighter-rouge\\">port</code>,默认值是 1935(RTMFP 服务器的默认端口),用于 CumulusServer 监听 RTMFP 请求。</li>\\n <li>UDP 缓冲区字节数 <code class=\\"language-plaintext highlighter-rouge\\">udpBufferSize</code>, allows to change the size in bytes of UDP reception and sending buffer. Increases this value if your operating system has a default value too lower for important loads.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAliveServer</code>, time in seconds for periodically sending packets keep-alive with server, 15s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAlivePeer</code>, time in seconds for periodically sending packets keep-alive between peers, 10s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.activated</code>, activate or not the edges server on the RTMFP server (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, CumulusServer stays a RTMFP server without edges ability (default value is false).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.port</code>, port for the edges server, to accept incoming new CumulusEdge instances (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, it’s the port 1936.</li>\\n</ul>\\n\\n<blockquote>\\n <p>Warning: This port will receive plain text request from edges, for this purpose it should not be made public. It’s very important for security consideration. It must be available only for CumulusEdge instances, and anything else.</p>\\n</blockquote>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.attemptsBeforeFallback</code>, number of CumulusEdge attempt connections before falling back to CumulusServer (see CumulusEdge, Scalability page for more details about CumulusEdge). By default the value is 2 (in practical, 2 attempts happens after 5 sec approximately).</li>\\n <li>SMTP IP 地址 <code class=\\"language-plaintext highlighter-rouge\\">smtp.host</code>, configure a SMTP host to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is localhost.</li>\\n <li>SMTP 端口 <code class=\\"language-plaintext highlighter-rouge\\">smtp.port</code>, configure a SMTP port to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 25.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">smtp.timeout</code>, configure a SMTP timeout session in seconds to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 60 seconds.</li>\\n <li>日志路径 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code>,默认是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/logsby</code>。</li>\\n <li>日志文件名称 <code class=\\"language-plaintext highlighter-rouge\\">logs.name</code>,默认是<code class=\\"language-plaintext highlighter-rouge\\">log</code>。</li>\\n</ul>\\n\\n<h4 id=\\"5启动\\">5、启动</h4>\\n\\n<p>Windows 下的启动方法为:</p>\\n\\n<pre><code class=\\"language-dos\\">$ CumulusServer.exe /registerService [/displayName=CumulusServer /description=\\"Open Source RTMFP Server\\" /startup=automatic]\\n</code></pre>\\n\\n<p>Unix-like 下的启动方法为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"o\\">[</span><span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>/var/run/CumulusServer.pid]\\n</code></pre></div></div>\\n\\n<p>具体地,我的启动命令为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>./CumulusServer.pid\\n</code></pre></div></div>\\n\\n<h4 id=\\"6基本使用\\">6、基本使用</h4>\\n\\n<p>本地 Flash client 可以通过如下语句连接:</p>\\n\\n<div class=\\"language-as highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">$</span> <span class=\\"kd\\">var</span> <span class=\\"nx\\">nc</span><span class=\\"o\\">:</span><span class=\\"nx\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nx\\">NetConnection</span><span class=\\"p\\">()</span><span class=\\"o\\">;</span><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost/\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>RTMFP默认是采用1935端口,如果你特别指定了其他端口,比如12345,请使用如下方式:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>nc.connect(\\"rtmfp://localhost:12345/\\");\\n</code></pre></div></div>\\n\\n<h4 id=\\"7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</h4>\\n\\n<p>启动CumulusServer后,会在可执行文件的目录下出现一个www目录,该目录的作用,就是作为 Server Application 的默认根目录。具体的对应关系如下:</p>\\n\\n<blockquote>\\n <p>rtmfp://host:port/ -&gt; [CumulusServer folder]/www/main.lua (root application)</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/myApplication -&gt; [CumulusServer folder]/www/myApplication/main.lua</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/Games/myGame -&gt; [CumulusServer folder]/www/Games/myGame/main.lua</p>\\n</blockquote>\\n\\n<p>另外要提醒的是,如果main.lua文件被修改,则不需要重启 CumulusServer,因为 Server Application 的创建是一种动态的方式。</p>\\n\\n<h3 id=\\"三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</h3>\\n\\n<p>下面的这个实例是在本地(Client 与 Server 位于同一机器上)测试的。</p>\\n\\n<h4 id=\\"1server-side\\">1、Server-side</h4>\\n\\n<h5 id=\\"11serverconfiguration\\">1.1、Server configuration</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span> <span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1935</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span><span class=\\"n\\">name</span> <span class=\\"o\\">=</span> <span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12applicationfile\\">1.2、Application file</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">function</span> <span class=\\"nf\\">onConnection</span><span class=\\"p\\">(</span><span class=\\"n\\">client</span><span class=\\"p\\">,</span><span class=\\"n\\">response</span><span class=\\"p\\">,</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">function</span> <span class=\\"nf\\">client</span><span class=\\"p\\">:</span><span class=\\"n\\">test</span><span class=\\"p\\">(</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">firstname</span> <span class=\\"o\\">=</span> <span class=\\"n\\">unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">arg</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"s2\\">\\"Hello \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">firstname</span><span class=\\"o\\">..</span><span class=\\"s2\\">\\" \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">name</span>\\n <span class=\\"k\\">end</span>\\n <span class=\\"k\\">end</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2client-side\\">2、Client-side</h4>\\n\\n<div class=\\"language-java highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\">// CumulusClient.as</span>\\n\\n<span class=\\"kn\\">package</span> <span class=\\"err\\">{</span>\\n <span class=\\"nn\\">import</span> <span class=\\"n\\">flash</span><span class=\\"o\\">.</span><span class=\\"na\\">display</span><span class=\\"o\\">.</span><span class=\\"na\\">Sprite</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetConnection</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetStream</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.Responder</span><span class=\\"o\\">;</span>\\n\\n <span class=\\"kd\\">public</span> <span class=\\"kd\\">class</span> <span class=\\"nc\\">CumulusClient</span> <span class=\\"kd\\">extends</span> <span class=\\"nc\\">Sprite</span> <span class=\\"o\\">{</span>\\n <span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">nc:</span><span class=\\"nc\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t<span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">ns:</span><span class=\\"nc\\">NetStream</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t\\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">CumulusClient</span><span class=\\"o\\">()</span> <span class=\\"o\\">{</span>\\n <span class=\\"n\\">nc</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nc\\">NetConnection</span><span class=\\"o\\">();</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">connect</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"rtmfp://localhost\\"</span><span class=\\"o\\">);</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">client</span> <span class=\\"o\\">=</span> <span class=\\"k\\">this</span><span class=\\"o\\">;</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">call</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"test\\"</span><span class=\\"o\\">,</span><span class=\\"k\\">new</span> <span class=\\"nc\\">Responder</span><span class=\\"o\\">(</span><span class=\\"n\\">onResult</span><span class=\\"o\\">,</span><span class=\\"n\\">onStatus</span><span class=\\"o\\">),</span> <span class=\\"s\\">\\"OpenRTMFP/Cumulus\\"</span><span class=\\"o\\">,</span> <span class=\\"s\\">\\"World\\"</span><span class=\\"o\\">)</span>\\n <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">close</span><span class=\\"o\\">():</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span> \\n\\t\\t\\t<span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">close</span><span class=\\"o\\">();</span>\\n \\t<span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onStatus</span><span class=\\"o\\">(</span><span class=\\"nl\\">status:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">status</span><span class=\\"o\\">.</span><span class=\\"na\\">description</span><span class=\\"o\\">)</span>\\n\\t <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onResult</span><span class=\\"o\\">(</span><span class=\\"nl\\">response:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">response</span><span class=\\"o\\">)</span> <span class=\\"c1\\">// expected to display \\"Hello World OpenRTMFP/Cumulus\\" </span>\\n\\t <span class=\\"o\\">}</span> \\n\\t<span class=\\"o\\">}</span>\\n<span class=\\"o\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3运行结果\\">3、运行结果</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Hello World OpenRTMFP/Cumulus\\n[SWF] CumulusClient.swf - 解压缩后为 1,776 个字节\\n[卸装 SWF] CumulusClient.swf\\n</code></pre></div></div>\\n\\n<h4 id=\\"4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</h4>\\n\\n<p>获取 Developer Key 的地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/index.jsp\\n</code></pre></div></div>\\n\\n<p>服务器配置信息:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Server: amd64 OS: Linux 2.6.18-028stab095.1\\nServer IP: 108.59.252.39\\nOpenRTMFP as of: 22.Feb.2012\\n</code></pre></div></div>\\n\\n<p>编写服务器段应用地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/manage_ssls.jsp\\n</code></pre></div></div>\\n\\n<p>快去试试吧 :)</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的第二篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-14T11:20:46+00:00\\" class=\\"by-line\\">14 Apr 2012, 广州 | 作者 麦克船长 | 总计 18890 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一cumulus-启动源码分析\\" id=\\"markdown-toc-一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数\\" id=\\"markdown-toc-1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</a></li>\\n <li><a href=\\"#2maincpp-中的-cumulusserver-构造函数\\" id=\\"markdown-toc-2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</a></li>\\n <li><a href=\\"#3maincpp-中的-cumulusserver-的-initialize-成员函数\\" id=\\"markdown-toc-3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</a></li>\\n <li><a href=\\"#4maincpp-中的-cumulusserver-的-main-成员函数\\" id=\\"markdown-toc-4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</a></li>\\n <li><a href=\\"#5cumulusserver-是-serverapplication-的子类\\" id=\\"markdown-toc-5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</a></li>\\n <li><a href=\\"#6serverapplication-是-application-的子类\\" id=\\"markdown-toc-6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</a></li>\\n <li><a href=\\"#7反初始化\\" id=\\"markdown-toc-7反初始化\\">7、反初始化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二cumulusserver-各项交互功能的源码解读\\" id=\\"markdown-toc-二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</a> <ul>\\n <li><a href=\\"#1命令行选项设定\\" id=\\"markdown-toc-1命令行选项设定\\">1、命令行选项设定</a></li>\\n <li><a href=\\"#2处理命令行选项\\" id=\\"markdown-toc-2处理命令行选项\\">2、处理命令行选项</a></li>\\n <li><a href=\\"#6dump-logs\\" id=\\"markdown-toc-6dump-logs\\">6、Dump logs</a></li>\\n <li><a href=\\"#3停止运行\\" id=\\"markdown-toc-3停止运行\\">3、停止运行</a></li>\\n <li><a href=\\"#4载入配置\\" id=\\"markdown-toc-4载入配置\\">4、载入配置</a></li>\\n <li><a href=\\"#5处理日志\\" id=\\"markdown-toc-5处理日志\\">5、处理日志</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三maincpp-的-main-函数源码分析\\" id=\\"markdown-toc-三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数中的-server\\" id=\\"markdown-toc-1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></a></li>\\n <li><a href=\\"#2maincpp-中-main-函数的-serverstart\\" id=\\"markdown-toc-2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></a></li>\\n <li><a href=\\"#3回顾一下整个启动流程\\" id=\\"markdown-toc-3回顾一下整个启动流程\\">3、回顾一下整个启动流程</a></li>\\n <li><a href=\\"#4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\" id=\\"markdown-toc-4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。首先要知道的是,OpenRTMFP/Cumulus 中使用到的库有 Poco、OpenSSL 和 Lua。</p>\\n\\n<h3 id=\\"一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</h4>\\n\\n<p>入口在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"kt\\">int</span> <span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">argv</span><span class=\\"p\\">[])</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先检查内存泄露,不过目前这个开发中的项目还没有实现这个功能,只是个空函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">DetectMemoryLeak</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后会创建一个匿名的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象,并调用其 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,该函数由 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 从 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 中继承而来,而 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 由是从 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 继承而来的。<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象调用 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,实际是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是调用 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的函数,而该 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。如果 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数被调用时抛出异常,则不会执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,但仍然会执行 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Runs the application by performing additional initializations</span>\\n <span class=\\"c1\\">// and calling the main() method.</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">CumulusServer</span><span class=\\"p\\">().</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"n\\">argv</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">()</span><span class=\\"o\\">:</span> <span class=\\"n\\">_helpRequested</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span> <span class=\\"c1\\">// 显示帮助信息</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_middle</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_isInteractive</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pLogFile</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</h4>\\n\\n<p>在执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数之前,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 会启动 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,传入的参数就是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 自己,可以猜到 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run</code> 方法中,调用该函数时的参数是 <code class=\\"language-plaintext highlighter-rouge\\">this</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">Application</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">self</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>调用父函数 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的初始化函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">self</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>再继续下面的源码分析之前,先要知道,根据 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> ,<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 类有一些内置的配置属性,如下:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.path</code>: 可执行文件的绝对路径;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.name</code>: 可执行文件的文件名(含扩展名);</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.baseName</code>: 可执行文件的文件名(不含扩展名)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.dir</code>: 可执行文件的所在目录;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.configDir</code>: 配置文件所在目录;</li>\\n</ul>\\n\\n<p>所以下面就读取了可执行文件的所在目录,其中第二个参数表示默认值(即当前目录):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>然后读取配置文件,目录为上一句所得到的 <code class=\\"language-plaintext highlighter-rouge\\">dir</code>,文件名(不含扩展名)为 <code class=\\"language-plaintext highlighter-rouge\\">application.basename</code> 内置配置属性值,其默认值为 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>,然后加上点和扩展名 <code class=\\"language-plaintext highlighter-rouge\\">.ini</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">dir</span>\\n <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.baseName\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"CumulusServer\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">+</span> <span class=\\"s\\">\\".ini\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这样就加载完配置了。然后查看当前的进程是从命令行运行的(命令行是交互的,所以是 interactive),还是以 daemon 方式运行的,这个函数是ServerApplication的一个成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_isInteractive</span> <span class=\\"o\\">=</span> <span class=\\"n\\">isInteractive</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后获取表示日志文件所在目录的字符串,其中 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code> 是外置配置属性(配置文件中),其默认值为上面获取到的可执行文件路径(一般为当前路径)与 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 的组合,即一般为当前目录下的 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"nf\\">logDir</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.directory\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"logs\\"</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>创建日志文件目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">logDir</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n\\n</code></pre></div></div>\\n\\n<p>日志文件绝对路径,<code class=\\"language-plaintext highlighter-rouge\\">logs</code> 为默认的日志文件名(不含扩展名的部分),扩展名用0:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logDir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span> <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.name\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\".\\"</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pLogFile</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nf\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"0\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>用日志流打开日志文件(方式为追加写入):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Logs</code> 是一个方法类(其中的 <code class=\\"language-plaintext highlighter-rouge\\">public</code> 函数都是静态的),<code class=\\"language-plaintext highlighter-rouge\\">SetLogger</code> 的作用就是将Logs中的似有静态成员设置为某个 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code> 对象(由于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLogger</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</h4>\\n\\n<p>OpenRTMFP/Cumulus 是一个基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的服务端应用(准确的说是基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::ServerApplication</code> 的服务端应用)。如果没有特殊的启动要求,可以调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco/Application.h</code> 中定义的宏 <code class=\\"language-plaintext highlighter-rouge\\">POCO_APP_MAIN</code> 来完成初始化、日志和启动(该宏会根据不同的平台启用不同的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数)。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数在调用完 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数后,会调用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,该 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的定义在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&gt;&amp;</span> <span class=\\"n\\">args</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>首先看是否是要求帮助信息,<code class=\\"language-plaintext highlighter-rouge\\">displayHelp</code> 是借助 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::HelpFormatter</code> 实现的,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数会在调用时将 <code class=\\"language-plaintext highlighter-rouge\\">_helpRequested</code> 设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_helpRequested</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">displayHelp</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是,则进入启动状态,首先创建一个 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServerParams</code> 对象 <code class=\\"language-plaintext highlighter-rouge\\">params</code>,用来存储 OpenRTMFP/CumulusServer 的基本配置信息。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">RTMFPServerParams</span> <span class=\\"n\\">params</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">params</code> 存储 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的端口号和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的端口号:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"port\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RTMFP_DEFAULT_PORT</span><span class=\\"o\\">+</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getBool</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.activated\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port must have a positive value if \\\\\\n edges.activated is true. Server edges is \\\\\\n disactivated.\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">=</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">_pCirrus</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">SocketAddress</code> 的成员,是封装IP地址和端口号的对象。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_middle</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>UDB 所使用的缓冲区大小:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"udpBufferSize\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"keepAliveServer\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"keepAlivePeer\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>失败前 CumulusEdge 的尝试次数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"edges.attemptsBeforeFallback\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> 函数是 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中必须调用的,意为等待终止运行的操作请求。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// wait for CTRL-C or kill</span>\\n <span class=\\"n\\">waitForTerminationRequest</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>一旦接收到终止操作的请求,就会执行下面这句,用以退出 OpenRTMFP/Cumulus 的运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Stop the server</span>\\n <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">stop</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">catch</code> 一些可能产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Configuration problem : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>OpenRTMFP/CumulusServer 停止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"n\\">Application</span><span class=\\"o\\">::</span><span class=\\"n\\">EXIT_OK</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 对其子类有如下要求:</p>\\n\\n<ul>\\n <li>Subsystems must be registered in the constructor.</li>\\n <li>All non-trivial initializations must be made in the <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>` method.</li>\\n <li>At the end of the <code class=\\"language-plaintext highlighter-rouge\\">main()</code> method, <code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> should be called.</li>\\n</ul>\\n\\n<h4 id=\\"6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</h4>\\n\\n<p>Application 对其子类的要求是,如下这些成员函数必须被覆盖:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> (the one-argument, protected variant):上一篇已介绍过。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code>:下面会介绍,Application 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会在调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">reinitialize()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">defineOptions()</code>:定义命令行启动选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">handleOption()</code>:响应相应的命令行选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">main()</code></li>\\n</ul>\\n\\n<h4 id=\\"7反初始化\\">7、反初始化</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的。<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code>,最后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code>。最后这个反初始化过程,在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 就是直接调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">uninitialize</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">uninitialize</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</h3>\\n\\n<h4 id=\\"1命令行选项设定\\">1、命令行选项设定</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的命令行选项有:<code class=\\"language-plaintext highlighter-rouge\\">log(l)</code>、<code class=\\"language-plaintext highlighter-rouge\\">dump(d)</code>、<code class=\\"language-plaintext highlighter-rouge\\">cirrus(c)</code>、<code class=\\"language-plaintext highlighter-rouge\\">middle(m)</code>、<code class=\\"language-plaintext highlighter-rouge\\">help(h)</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">OptionSet</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">options</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>设定日志级别(0 - 8,默认是 6,表示 info 级别)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"l\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Log level argument, must be beetween 0 and 8 : \\\\\\n nothing, fatal, critic, error, warn, note, info, debug, trace. \\\\\\n Default value is 6 (info), all logs until info level are displayed.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">argument</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"level\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>其他一些选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"d\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Enables packet traces in logs. Optional arguments \\\\\\n are 'middle' or 'all' respectively to displays just middle packet \\\\\\n process or all packet process. If no argument is given, just outside \\\\\\n packet process will be dumped.\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"middle|all\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"c\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Cirrus address to activate a 'man-in-the-middle' \\\\\\n developer mode in bypassing flash packets to the official cirrus \\\\\\n server of your choice, it's a instable mode to help Cumulus developers, \\\\\\n </span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\">p2p.rtmfp.net:10000</span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\"> for example. By adding the 'dump' argument, \\\\\\n you will able to display Cirrus/Flash packet exchange in your logs \\\\\\n (see 'dump' argument).\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"address\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"m\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"Enables a 'man-in-the-middle' developer mode \\\\\\n between two peers. It's a instable mode to help Cumulus developers. \\\\\\n By adding the 'dump' argument, you will able to display Flash/Flash \\\\\\n packet exchange in your logs (see 'dump' argument).\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>显示帮助信息的选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"h\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Displays help information about command-line usage.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">OptionSet</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::OptionSet</code>,调用addOption可以向其中增加选项Option。其中required和repeatable表示:</p>\\n\\n<p>Sets whether the option is required (flag == true) or optional (flag == false).\\nReturns true if the option can be specified more than once, or false if at most once.\\n当需要显示帮助信息时,调用如下函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">displayHelp</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">HelpFormatter</span> <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setCommand</span><span class=\\"p\\">(</span><span class=\\"n\\">commandName</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setUsage</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"OPTIONS\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setHeader</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer, open source RTMFP server\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">cout</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setCommand()</code>: Sets the command name.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setUsage()</code>: Sets the usage string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setHeader()</code>: Sets the header string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">format()</code>: Writes the formatted help text to the given stream.</li>\\n</ul>\\n\\n<h4 id=\\"2处理命令行选项\\">2、处理命令行选项</h4>\\n\\n<p>参数是选项名和选项值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是帮助:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_helpRequested</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">URI</span> <span class=\\"n\\">uri</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"rtmfp://\\"</span><span class=\\"o\\">+</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">SocketAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getHost</span><span class=\\"p\\">(),</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getPort</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' : the exchange will bypass to '%s'\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' error : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">message</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是dump日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"all\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">ALL</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">MIDDLE</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">EXTERNAL</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是log,表示设定日志级别:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLevel</span><span class=\\"p\\">(</span><span class=\\"n\\">atoi</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">()));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"6dump-logs\\">6、Dump logs</h4>\\n\\n<p>先加一个作用域锁,然后再向日志流写数据。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">dumpHandler</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">cout</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>调用 manageLogFile,主要做一些日志大小超出限制的处理。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">manageLogFile</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先判断是否超过日志文件的大小上线,LOG_SIZE是1000000字节(即约 1 MB)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">getSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">LOG_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">num</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>打开新日志文件:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span> <span class=\\"nf\\">file</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"10\\"</span><span class=\\"p\\">);</span>\\n\\n</code></pre></div></div>\\n\\n<p>如果该文件已经存在,则先删除:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">remove</span><span class=\\"p\\">();</span>\\n\\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">--</span><span class=\\"n\\">num</span> <span class=\\"o\\">&gt;=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">renameTo</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span> <span class=\\"o\\">+</span> <span class=\\"mi\\">1</span><span class=\\"p\\">));</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span> \\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3停止运行\\">3、停止运行</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code>,该类中有纯虚函数 <code class=\\"language-plaintext highlighter-rouge\\">kill()</code> 需要被实现,于是有:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">kill</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">terminate</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code> 的定义在 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller.h</code> 中,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ApplicationKiller</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n \\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">kill</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4载入配置\\">4、载入配置</h4>\\n\\n<p>在initialize()函数中调用,上一篇已提到过。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">path</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5处理日志\\">5、处理日志</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">logHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">TID</span> <span class=\\"n\\">threadId</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">threadName</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Priority</span> <span class=\\"n\\">priority</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">filePath</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">long</span> <span class=\\"n\\">line</span><span class=\\"p\\">,</span> \\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">text</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>作用域锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">Path</span> <span class=\\"nf\\">path</span><span class=\\"p\\">(</span><span class=\\"n\\">filePath</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">string</span> <span class=\\"n\\">file</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getExtension</span><span class=\\"p\\">()</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"lua\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">directory</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">depth</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是命令行交互模式(即不是 daemon 模式):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_isInteractive</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">printf</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"%s %s[%ld] %s</span><span class=\\"se\\">\\\\n</span><span class=\\"s\\">\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">],</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getBaseName</span><span class=\\"p\\">()).</span><span class=\\"n\\">c_str</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">line</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">text</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>向日志流输出一句日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">DateTimeFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">LocalDateTime</span><span class=\\"p\\">(),</span><span class=\\"s\\">\\"%d/%m %H:%M:%S.%c \\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">]</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'\\\\t'</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadName</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'('</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadId</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\")</span><span class=\\"se\\">\\\\t</span><span class=\\"s\\">\\"</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getFileName</span><span class=\\"p\\">())</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'['</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">line</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\"] \\"</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">text</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">endl</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>日志文件的善后处理(主要处理文件大小限制可能产生的问题):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中真正启动的是 <code class=\\"language-plaintext highlighter-rouge\\">server</code>,它继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code>,而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code> 又继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Gateway</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Handler</code>。而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code> 继承自 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Runnable</code>,所以其是一个可以运行的线程。在 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 中,这是主线程。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span> <span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n<span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/Server.h</code> 中定义的,其构造函数的原型为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>个参数含义如下:</p>\\n\\n<blockquote>\\n <p>The Path Root for the Server Application Killer for Termanting the Server Application Server Configuration</p>\\n</blockquote>\\n\\n<p>距离来说,在我的 Worksapce 中:</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">root</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 构造函数的初始化列表极长:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"o\\">::</span><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">)</span> \\n <span class=\\"o\\">:</span> <span class=\\"n\\">_blacklist</span><span class=\\"p\\">(</span><span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"blacklist\\"</span><span class=\\"p\\">,</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_applicationKiller</span><span class=\\"p\\">(</span><span class=\\"n\\">applicationKiller</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_hasOnRealTime</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pService</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">luaMail</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"o\\">=</span><span class=\\"n\\">Script</span><span class=\\"o\\">::</span><span class=\\"n\\">CreateState</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.host\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"localhost\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">SMTPSession</span><span class=\\"o\\">::</span><span class=\\"n\\">SMTP_PORT</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.timeout\\"</span><span class=\\"p\\">,</span><span class=\\"mi\\">60</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>下面调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::File</code> 创建目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">((</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">WWWPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"www\\"</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>因为 <code class=\\"language-plaintext highlighter-rouge\\">roor</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 目录,所以 <code class=\\"language-plaintext highlighter-rouge\\">WWWPath</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/www</code> 目录。然后初始化 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code>,这个 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code> 是和 Lua 有关的东东,这里暂不细说,先知道与 Lua 相关就好。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Service</span><span class=\\"o\\">::</span><span class=\\"n\\">InitGlobalTable</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>下面就涉及到了 Lua script 了:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SCRIPT_BEGIN</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">SCRIPT_CREATE_PERSISTENT_OBJECT</span><span class=\\"p\\">(</span><span class=\\"n\\">Invoker</span><span class=\\"p\\">,</span><span class=\\"n\\">LUAInvoker</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">readNextConfig</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"n\\">configurations</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">lua_setglobal</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"cumulus.configs\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">SCRIPT_END</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN</code>、<code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_CREATE_PERSISTENT_OBJECT和SCRIPT_END</code> 都是宏,其定义在 <code class=\\"language-plaintext highlighter-rouge\\">Script.h</code> 文件中,如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define SCRIPT_BEGIN(STATE) \\\\\\n if (lua_State* __pState = STATE) { \\\\\\n const char* __error=NULL;\\n \\n#define SCRIPT_CREATE_PERSISTENT_OBJECT(TYPE,LUATYPE,OBJ) \\\\\\n Script::WritePersistentObject&lt;TYPE,LUATYPE&gt;(__pState,OBJ); \\\\\\n lua_pop(__pState,1);\\n \\n#define SCRIPT_END }\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN和SCRIPT_END</code> 经常用到,当与 Lua 相关的操作出现时,都会以这两个宏作为开头和结尾。</p>\\n\\n<h4 id=\\"2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServerParams</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">params</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 正在运行,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer server is yet running, call stop method before\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定端口号,如果端口号为 0,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must have a positive value\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusEdge</code> 的端口号,如果其端口号与 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusSever</code> 端口号相同,则返回并终止启动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"n\\">_edgesPort</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must different than RTMFPServer edges.port\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>Cirrus:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">2000000</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// 2 sec by default</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">Target</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// no waiting, direct process in the middle case!</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode with server %s \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pCirrus</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">address</span><span class=\\"p\\">.</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode between peers \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>UDP Buffer:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span> <span class=\\"o\\">?</span> \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">getReceiveBufferSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Socket buffer receving/sending size = %u/%u\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">threadPriority</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>启动线程,进入循环运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>上句具体的源码实现为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果在运行则返回并终止启动。然后加一个局部锁。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果不得不join()到主线程中,那就join()吧</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后就运行这个线程吧:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3回顾一下整个启动流程\\">3、回顾一下整个启动流程</h4>\\n\\n<p>到此我们先回顾一下启动过程:</p>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的启动入口 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数开始,创建 <code class=\\"language-plaintext highlighter-rouge\\">Server</code> 对象并启动(调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 函数)。<code class=\\"language-plaintext highlighter-rouge\\">Server::start()</code> 中调用其父类(<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code>)的父类(<code class=\\"language-plaintext highlighter-rouge\\">Startable</code>)的方法 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 开启线程。\\n调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 函数后,开启线城时传入的参数为 <code class=\\"language-plaintext highlighter-rouge\\">*this</code>,所以就会运行 <code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code>;</p>\\n\\n<h4 id=\\"4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code> 调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,但这个函数被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖,所以会运行 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>,其源码如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果CumulusEdge:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP edges server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">onStart</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>运行线程:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>处理异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果跳出了,则终止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">onStop</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server stops\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>该函数内部又会调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,该函数调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>它是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 实现。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 会调用 <code class=\\"language-plaintext highlighter-rouge\\">void run(const volatile bool&amp; terminate)</code> 方法,该方法被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">...</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</title>\\n \\t<meta name=\\"description\\" content=\\"CumulusServer 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 CumulusEdge 和 CumulusServer 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 CumulusServer 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-15T14:26:58+00:00\\" class=\\"by-line\\">15 Apr 2012, 广州 | 作者 麦克船长 | 总计 3844 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1绑定地址\\" id=\\"markdown-toc-1绑定地址\\">1、绑定地址</a></li>\\n <li><a href=\\"#2cumulusserver-接收数据\\" id=\\"markdown-toc-2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</a></li>\\n <li><a href=\\"#3如果-cumulusedge-端口存在且-edge-socket-可用\\" id=\\"markdown-toc-3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</a></li>\\n <li><a href=\\"#4cumulusserver-和-cumulusedge-的-socket-都没有数据\\" id=\\"markdown-toc-4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</a></li>\\n <li><a href=\\"#5发送方的-ip-被禁\\" id=\\"markdown-toc-5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</a></li>\\n <li><a href=\\"#6数据包长度小于可能的最小值12\\" id=\\"markdown-toc-6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</a></li>\\n</ul>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。</p>\\n\\n<p>本所要介绍的这个主循环在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(const volatile bool&amp; terminate)</code> 函数中。RTMFPServer覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"1绑定地址\\">1、绑定地址</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">address</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">绑定</span><span class=\\"n\\">CumulusEdge</span><span class=\\"err\\">的</span> <span class=\\"n\\">IP</span> <span class=\\"err\\">地址和端口:</span>\\n\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">edgesAddress</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>发送者(Client)的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"n\\">sender</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">buff</span><span class=\\"p\\">[</span><span class=\\"n\\">PACKETRECV_SIZE</span><span class=\\"p\\">];</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">stop</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">idle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">realTime</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 读取:把数据存到 <code class=\\"language-plaintext highlighter-rouge\\">buff</code>,把发送者地址赋给 <code class=\\"language-plaintext highlighter-rouge\\">sender</code>,把所读长度返回给 <code class=\\"language-plaintext highlighter-rouge\\">size</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>处理 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span> <span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span> <span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span> <span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">Edge</span><span class=\\"o\\">*</span> <span class=\\"n\\">pEdge</span> <span class=\\"o\\">=</span> <span class=\\"n\\">edges</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pEdge</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pEdge</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 空闲:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">idle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>主线程等待一秒。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">isElapsed</span><span class=\\"p\\">(</span><span class=\\"n\\">_freqManage</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>Just middle session</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Sessions</span><span class=\\"o\\">::</span><span class=\\"n\\">Iterator</span> <span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Middle</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMiddle</span> <span class=\\"o\\">=</span> <span class=\\"k\\">dynamic_cast</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Middle</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMiddle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pMiddle</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isBanned</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">INFO</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Data rejected because client %s is banned\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">().</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">size</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">RTMFP_MIN_PACKET_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Invalid packet\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">PacketReader</span> <span class=\\"nf\\">packet</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Session</span><span class=\\"o\\">*</span> <span class=\\"n\\">pSession</span> <span class=\\"o\\">=</span> <span class=\\"n\\">findSession</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFP</span><span class=\\"o\\">::</span><span class=\\"n\\">Unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">));</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">checked</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">commitCookie</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pSession</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>给 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 或者自己(<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>)的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">setEndPoint</span><span class=\\"p\\">(</span><span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">?</span> <span class=\\"n\\">_edgesSocket</span> <span class=\\"o\\">:</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">,</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">delete</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T02:04:55+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 30820 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一amf-数据类型定义\\" id=\\"markdown-toc-一amf-数据类型定义\\">一、AMF 数据类型定义</a> <ul>\\n <li><a href=\\"#1数据类型\\" id=\\"markdown-toc-1数据类型\\">1、数据类型</a></li>\\n <li><a href=\\"#2undefined-type\\" id=\\"markdown-toc-2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</a></li>\\n <li><a href=\\"#3null-type\\" id=\\"markdown-toc-3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</a></li>\\n <li><a href=\\"#4false-type\\" id=\\"markdown-toc-4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</a></li>\\n <li><a href=\\"#5true-type\\" id=\\"markdown-toc-5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</a></li>\\n <li><a href=\\"#6integer-type\\" id=\\"markdown-toc-6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</a></li>\\n <li><a href=\\"#7double-type\\" id=\\"markdown-toc-7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</a></li>\\n <li><a href=\\"#8string-type\\" id=\\"markdown-toc-8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</a></li>\\n <li><a href=\\"#9xmldocument-type\\" id=\\"markdown-toc-9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</a></li>\\n <li><a href=\\"#10date-type\\" id=\\"markdown-toc-10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</a></li>\\n <li><a href=\\"#11array-type\\" id=\\"markdown-toc-11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</a></li>\\n <li><a href=\\"#12object-type\\" id=\\"markdown-toc-12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</a></li>\\n <li><a href=\\"#13xml-type\\" id=\\"markdown-toc-13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</a></li>\\n <li><a href=\\"#14bytearray-type\\" id=\\"markdown-toc-14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</a></li>\\n <li><a href=\\"#15amf3-的使用\\" id=\\"markdown-toc-15amf3-的使用\\">15、AMF3 的使用</a> <ul>\\n <li><a href=\\"#151netconnection-and-amf-3\\" id=\\"markdown-toc-151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</a></li>\\n <li><a href=\\"#152netconnection-in-actionscript-30\\" id=\\"markdown-toc-152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</a></li>\\n <li><a href=\\"#153bytearray-idatainput-and-idataoutput\\" id=\\"markdown-toc-153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二binaryreaderwriter\\" id=\\"markdown-toc-二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></a> <ul>\\n <li><a href=\\"#1amf3-数据格式基础\\" id=\\"markdown-toc-1amf3-数据格式基础\\">1、AMF3 数据格式基础</a></li>\\n <li><a href=\\"#2序列化\\" id=\\"markdown-toc-2序列化\\">2、序列化</a></li>\\n <li><a href=\\"#3反序列化\\" id=\\"markdown-toc-3反序列化\\">3、反序列化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三packetreaderwriter\\" id=\\"markdown-toc-三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></a> <ul>\\n <li><a href=\\"#1packetreader\\" id=\\"markdown-toc-1packetreader\\">1、PacketReader</a> <ul>\\n <li><a href=\\"#11封装-memoryinputstream\\" id=\\"markdown-toc-11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></a></li>\\n <li><a href=\\"#12收缩缓冲区\\" id=\\"markdown-toc-12收缩缓冲区\\">1.2、收缩缓冲区</a></li>\\n <li><a href=\\"#13构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-13构造函数拷贝构造函数和析构函数\\">1.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2packetwriter\\" id=\\"markdown-toc-2packetwriter\\">2、<code class=\\"language-plaintext highlighter-rouge\\">PacketWriter</code></a> <ul>\\n <li><a href=\\"#21封装memoryoutputstream\\" id=\\"markdown-toc-21封装memoryoutputstream\\">2.1、封装<code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code></a></li>\\n <li><a href=\\"#22封装-binarywriter\\" id=\\"markdown-toc-22封装-binarywriter\\">2.2、封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code></a></li>\\n <li><a href=\\"#23构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-23构造函数拷贝构造函数和析构函数\\">2.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四amfreader\\" id=\\"markdown-toc-四amfreader\\">四、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code></a> <ul>\\n <li><a href=\\"#1objectdef\\" id=\\"markdown-toc-1objectdef\\">1、<code class=\\"language-plaintext highlighter-rouge\\">ObjectDef</code></a></li>\\n <li><a href=\\"#2amfreader-定义\\" id=\\"markdown-toc-2amfreader-定义\\">2、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 定义</a> <ul>\\n <li><a href=\\"#21构造函数析构函数\\" id=\\"markdown-toc-21构造函数析构函数\\">2.1、构造函数、析构函数</a></li>\\n <li><a href=\\"#22简单封装-packetreader-的一些函数\\" id=\\"markdown-toc-22简单封装-packetreader-的一些函数\\">2.2、简单封装 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 的一些函数</a></li>\\n <li><a href=\\"#23设置-gptr-位置\\" id=\\"markdown-toc-23设置-gptr-位置\\">2.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 位置</a></li>\\n <li><a href=\\"#24判断类型\\" id=\\"markdown-toc-24判断类型\\">2.4、判断类型</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3解析-as3-null\\" id=\\"markdown-toc-3解析-as3-null\\">3、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Null</code></a></li>\\n <li><a href=\\"#4解析-as3-number\\" id=\\"markdown-toc-4解析-as3-number\\">4、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Number</code></a></li>\\n <li><a href=\\"#5解析-as3-integer\\" id=\\"markdown-toc-5解析-as3-integer\\">5、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code></a></li>\\n <li><a href=\\"#6解析-as3-boolean\\" id=\\"markdown-toc-6解析-as3-boolean\\">6、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code></a></li>\\n <li><a href=\\"#7开始引用与结束引用\\" id=\\"markdown-toc-7开始引用与结束引用\\">7、开始引用与结束引用</a></li>\\n <li><a href=\\"#8解析-as3-bytearray\\" id=\\"markdown-toc-8解析-as3-bytearray\\">8、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code></a></li>\\n <li><a href=\\"#9解析-as3-date\\" id=\\"markdown-toc-9解析-as3-date\\">9、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Date</code></a></li>\\n <li><a href=\\"#10解析-as3-dictionary\\" id=\\"markdown-toc-10解析-as3-dictionary\\">10、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Dictionary</code></a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。</p>\\n\\n<h3 id=\\"一amf-数据类型定义\\">一、AMF 数据类型定义</h3>\\n\\n<h4 id=\\"1数据类型\\">1、数据类型</h4>\\n\\n<p>各种数据类型的标示都在 AMF.h 中定义为宏</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cp\\">#define AMF_NUMBER 0x00 // 浮点数\\n#define AMF_BOOLEAN 0x01 // 布尔型\\n#define AMF_STRING 0x02 // 字符串\\n#define AMF_BEGIN_OBJECT 0x03 // 对象,开始\\n#define AMF_NULL 0x05 // null\\n#define AMF_UNDEFINED 0x06\\n#define AMF_REFERENCE 0x07\\n#define AMF_MIXED_ARRAY 0x08\\n#define AMF_END_OBJECT 0x09 // 对象,结束\\n#define AMF_BEGIN_TYPED_OBJECT 0x10\\n#define AMF_STRICT_ARRAY 0x0A\\n#define AMF_DATE 0x0B // 日期\\n#define AMF_LONG_STRING 0x0C // 字符串\\n#define AMF_UNSUPPORTED 0x0D\\n</span> \\n<span class=\\"cp\\">#define AMF_AVMPLUS_OBJECT 0x11\\n#define AMF_END 0xFF\\n</span> \\n<span class=\\"cp\\">#define AMF3_UNDEFINED 0x00\\n#define AMF3_NULL 0x01\\n#define AMF3_FALSE 0x02\\n#define AMF3_TRUE 0x03\\n#define AMF3_INTEGER 0x04\\n#define AMF3_NUMBER 0x05\\n#define AMF3_STRING 0x06\\n#define AMF3_DATE 0x08\\n#define AMF3_ARRAY 0x09\\n#define AMF3_OBJECT 0x0A\\n#define AMF3_BYTEARRAY 0x0C\\n#define AMF3_DICTIONARY 0x11\\n</span></code></pre></div></div>\\n\\n<p>并定义了一个枚举类表示数据类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">AMF</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"k\\">enum</span> <span class=\\"n\\">Type</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Null</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Boolean</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Integer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Number</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">String</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Date</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Array</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Object</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ByteArray</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Dictionary</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RawObjectContent</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">End</span>\\n <span class=\\"p\\">};</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型标记表示,用于编码布尔值 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</h4>\\n\\n<p>true 类型由 true 类型标记表示,用于编码布尔值 true。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</h4>\\n\\n<p>在 AMF 3 中,整数使用可变长度的无符号 29 位整数进行序列化。ActionScript 3.0 中的整数类型 - 有符号 <code class=\\"language-plaintext highlighter-rouge\\">int</code> 类型和无符号 <code class=\\"language-plaintext highlighter-rouge\\">uint</code> 类型 - 也使用 29 位在 AVM+中表示。如果无符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">uint</code>) 的值大于等于 229 或者如果有符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">int</code>) 的值大于等于 228,则它将被 AVM+ 表示为 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型,并使用 AMF 3 double 类型进行序列化。</p>\\n\\n<h4 id=\\"7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</h4>\\n\\n<p>AMF 3 的 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型与 AMF 0 的 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 类型编码方式相同。此类型用于编码 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 或值大于等于 228 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">int</code> 或值大于等于 229 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">uint</code>。编码值始终是网络字节顺序中的 8 字节 IEEE-754 双精度浮点值 (低内存中的符号位)。</p>\\n\\n<h4 id=\\"8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</h4>\\n\\n<p>ActionScript String 值使用 AMF 3 中的单个 string 类型表示 - AMF 0 中的 <code class=\\"language-plaintext highlighter-rouge\\">string</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">long string</code> 类型的概念不再使用。可以使用对隐式字符串引用表中的索引将字符串作为先前发生的字符串的引用发送。字符串使用 UTF-8 编码 - 但是头可以描述字符串文本或字符串引用。空字符串永远不会作为引用发送。</p>\\n\\n<h4 id=\\"9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了新的 XML 类型 (参见 3.13),但是旧版的 XMLDocument 类型在语言中被保留为 <code class=\\"language-plaintext highlighter-rouge\\">flash.xml.XMLDocument</code>。与 AMF 0 类似,<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 的结构需要扁平化为字符串表示以进行序列化。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。XMLDocuments 可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</h4>\\n\\n<p>在 AMF 3 中,ActionScript Date 简单地作为自 1970 年 1 月 1 日午夜 (UTC 时区) 以来的毫秒数进行序列化。不发送本地时区信息。可以使用对隐式对象引用表中的索引将日期作为先前发生的日期实例的引用发送。</p>\\n\\n<h4 id=\\"11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</h4>\\n\\n<p>ActionScript 数组的类型和在数组中的位置是基于它们的索引性质描述的。以下表格概述了这些术语的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">strict</code>:仅包含序数(数字)索引</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">dense</code>:序数索引从 0 开始,并且在连续索引之间不存在间隙(即,从 0 到数组长度的每一个索引都被定义了)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">sparse</code>:包含至少两个索引之间的一个间隙</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">associative</code>:包含至少一个非序数(字符串)索引(有时称为 ECMA 数组)</li>\\n</ul>\\n\\n<p>AMF 将数组分为两部分,密集部分和关联部分。关联部分的二进制表示由名称/值对(可能没有)终止的空字符串。密集部分的二进制表示由密集部分的大小(可能为零)以及有序的值列表(可能没有)组成。在 AMF 中写入的顺序是密集部分的大小,一个以空字符串终止的名称/值对列表,然后是大小的值。数组可以通过使用隐式对象引用表的索引作为先前发生的数组的引用来发送。</p>\\n\\n<h4 id=\\"12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</h4>\\n\\n<p>AMF 3 中有一种类型用于处理 ActionScript 对象和自定义用户类。使用术语 “traits” 来描述类的定义特征。除了 “anonymous” 对象和 “typed” 对象,ActionScript 3.0 还引入了两个进一步的 traits 来描述如何序列化对象,即 “dynamic” 和 “externalizable”。以下表格概述了这些术语和它们的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Anonymous</code>:实际的 ActionScript 对象类型的实例或没有注册别名的类的实例(在反序列化时将其视为对象)。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Typed</code>:具有注册别名的类的实例。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Dynamic</code>:具有动态特征声明的类定义的实例;可以在运行时动态地从实例中添加和删除公共变量成员。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Externalizable</code>:实现 flash.utils.IExternalizable 的类的实例,它完全控制其成员的序列化(特征信息中不包含属性名)。</li>\\n</ul>\\n\\n<p>在这些特征之外,对象的特征信息还可能包括在类上定义的一组公共变量和公共可读写属性名称(即不是函数的公共成员)。成员名称的顺序很重要,因为在特征信息之后的成员值将按照完全相同的顺序出现。这些成员被视为密封成员,因为它们是由类型明确定义的。</p>\\n\\n<p>如果类型是动态的,则在密封成员之后可以包括一个进一步的部分,该部分将动态成员列为名称/值对。当遇到空字符串名称时,继续读取动态成员。</p>\\n\\n<p>对象可以通过使用隐式对象引用表中的索引来作为先前发生对象的引用。此外,还可以通过使用隐式特征引用表中的索引将特征信息发送为先前发生的一组特征的引用。</p>\\n\\n<h4 id=\\"13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了一种新的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型,支持 E4X 语法。为了序列化,需要将 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型展平成字符串表示形式。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。<code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 XML 实例的引用发送。请注意,这种编码对 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 的使用造成了一些理论限制。每个 UTF-8 编码的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例的字节长度最大为 228-1 字节(大约 256 MB)。</p>\\n\\n<h4 id=\\"14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</h4>\\n\\n<p>用于保存字节数组,即 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>。AMF 3 使用可变长度编码 29 位整数序列化此类型,其中包括字节长度前缀,然后是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 的原始字节。<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"15amf3-的使用\\">15、AMF3 的使用</h4>\\n\\n<h5 id=\\"151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</h5>\\n\\n<p>除了序列化 ActionScript 类型外,AMF 还可用于远程服务的异步调用。可使用简单的消息结构将一批请求发送到远程端点。此消息结构的格式为 AMF 0(参见[AMF0])。可以使用特殊的 <code class=\\"language-plaintext highlighter-rouge\\">avmplus-object-marker</code> 类型将上下文头值或消息正文切换到 AMF 3 编码。</p>\\n\\n<h5 id=\\"152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</h5>\\n\\n<p>在 ActionScript 3.0 中,NetConnection 的限定类名是 flash.net.NetConnection。这个类仍然使用响应器来处理远程端点的结果和状态响应,但是现在需要强类型的 Responder 类。完全限定的类名是 flash.net.Responder。除了正常的结果和状态响应之外,NetConnection 还会分发事件,开发人员可以添加监听器。下面是这些事件的概述:</p>\\n\\n<ul>\\n <li>当异常异步抛出时触发,例如来自本机异步代码。</li>\\n <li>当输入或输出错误导致网络操作失败时触发。</li>\\n <li>当 NetConnection 对象报告其状态或错误条件时触发。</li>\\n <li>如果对 NetConnection.call() 的调用尝试连接到调用者安全沙箱外的服务器,则会触发。</li>\\n</ul>\\n\\n<h5 id=\\"153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></h5>\\n\\n<p>ActionScript 3.0 引入了一种新类型,用于支持以字节数组形式处理原始数据,即 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.ByteArray</code>。为了协助 ActionScript 对象序列化和复制,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实现了 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataInput</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataOutput</code>。这些接口指定了帮助将常见类型写入字节流的实用方法。两个感兴趣的方法是 <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput.writeObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">IDataInput.readObject</code>。这些方法使用 AMF 编码对象。使用的 AMF 版本由 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray.objectEncoding</code> 方法控制,该方法可以设置为 AMF 3 或 AMF 0。枚举类型 <code class=\\"language-plaintext highlighter-rouge\\">flash.net.ObjectEncoding</code> 包含 AMF 版本的常量:分别为 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF0</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF3</code>。</p>\\n\\n<p>请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray.writeObject</code> 使用一个版本的 AMF 对整个对象进行编码。与 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 不同,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 不会从 AMF 0 开始,然后将 <code class=\\"language-plaintext highlighter-rouge\\">objectEncoding</code> 属性设置为 AMF 3 并切换到 AMF 3。还请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 为每个 <code class=\\"language-plaintext highlighter-rouge\\">readObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">writeObject</code> 调用使用新的对象、对象特征和字符串的隐式引用表。</p>\\n\\n<h3 id=\\"二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></h3>\\n\\n<h4 id=\\"1amf3-数据格式基础\\">1、AMF3 数据格式基础</h4>\\n\\n<p>首先介绍一下变长整数(Variable Length Integer),比如 UInt32 如下。</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档,这是一种压缩方式的整数存储,且每一字节都对后面的数据具有预知作用。那么字符串如何处理呢?下面是字符串的处理方式,AMF0 和 AMF3 都才用 UTF-8 编码方式,并做如下压缩处理:</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档。</p>\\n\\n<h4 id=\\"2序列化\\">2、序列化</h4>\\n\\n<p>序列化包括 8 位、16 位、32 位,以及 UTF-8 和 UTF-16(I guess)编码的 String,还有原生数据(Raw Data)、变长无符号整数(Variable Length Unsigned Integer)以及 IP 地址。所谓序列化就是按照指定格式编写各种对象、基础数据类型值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryWriter</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Net</span><span class=\\"o\\">::</span><span class=\\"n\\">SocketAddress</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryWriter</span> <span class=\\"n\\">BinaryWriterNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>请注意其中名为 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriterNull</code> 的成员。构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">writeRaw</code> 是简单地封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter::writeRaw()</code>,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入整数实现如下,用的是从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的重载运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span> \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入变长整数,这段代码含义也一目了然,就是读取变长无符号 32 位整数、64 位整数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">21</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span> <span class=\\"c1\\">// 4 bytes maximum</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">22</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">63</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Can give 10 bytes!</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">shift</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入 IP 地址的两个函数暂略。</p>\\n\\n<h4 id=\\"3反序列化\\">3、反序列化</h4>\\n\\n<p>反序列化就是从指定格式的数据中读出各类型的数据值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryReader</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitEncoded</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString8</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString16</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">read16</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read32</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryReader</span> <span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造与析构函数都很简单:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">)</span> <span class=\\"o\\">:</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取原生数据(Raw Data):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写整数,用的是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter</code> 的重载运算符:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读写整数依旧使用从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt8</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt16</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read16</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read32</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取变长整数,分别针对 <code class=\\"language-plaintext highlighter-rouge\\">UInt32</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">UInt64</code>,要理解 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 的变长整数才能理解这个:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt64</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt64</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></h3>\\n\\n<h4 id=\\"1packetreader\\">1、PacketReader</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define PACKETRECV_SIZE 2048\\nclass PacketReader: public BinaryReader {\\npublic:\\n PacketReader(const Poco::UInt8* buffer,Poco::UInt32 size);\\n PacketReader(PacketReader&amp;);\\n virtual ~PacketReader();\\n const Poco::UInt32 fragments;\\n Poco::UInt32 available(); // 可读字节数\\n Poco::UInt8* current();\\n Poco::UInt32 position(); // 获取当前的相对位置(相对于起始位置的)\\n void reset(Poco::UInt32 newPos = 0); // 设定当前位置\\n void shrink(Poco::UInt32 rest);\\n void next(Poco::UInt32 size);\\nprivate:\\n MemoryInputStream _memory;\\n};\\n</code></pre></div></div>\\n\\n<h6 id=\\"11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">current</code>:当前绝对位置(内存地址)</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code>:当前位置(绝对位置)减去缓冲区起始位置</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"12收缩缓冲区\\">1.2、收缩缓冲区</h6>\\n\\n<p>封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">resize</code>。不过由于前面的 <code class=\\"language-plaintext highlighter-rouge\\">if</code> 语句影响,传给 resize 的参数一定不会大于缓冲区的当前大小。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">shrink</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">rest</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">rest</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">available</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"rest %u more upper than available %u bytes\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">rest</span><span class=\\"p\\">,</span><span class=\\"n\\">available</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">rest</span> <span class=\\"o\\">=</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"o\\">+</span> <span class=\\"n\\">rest</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"13构造函数拷贝构造函数和析构函数\\">1.3、构造函数、拷贝构造函数和析构函数</h6>\\n\\n<p>构造函数先调用父类 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReader</code> 的构造函数,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">fragments</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">_memory</code> 输入流的缓冲区。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">fragments</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"c1\\">// Consctruction by copy</span>\\n<span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">fragments</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">fragments</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">PacketReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2packetwriter\\">2、<code class=\\"language-plaintext highlighter-rouge\\">PacketWriter</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PacketWriter</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">BinaryWriter</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">good</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clear</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">limit</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryOutputStream</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pOther</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"21封装memoryoutputstream\\">2.1、封装<code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">good</code>:不过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code> 也是封装的 <code class=\\"language-plaintext highlighter-rouge\\">std::ostream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">good</code> 函数,True if no error flags are set.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">good</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">good</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">written</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">length</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code>:设置缓冲区的指针位置,即 <code class=\\"language-plaintext highlighter-rouge\\">position</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code>:移动缓冲区指针</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code>:返回缓冲区的起始地址</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">clear</code>:其实就是修改 written 和 position,使得指定位置后面的数据在以后写的时候可以被覆盖,并不是真正的清除。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">clear</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">limit</code>:封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">limit</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">length</span> <span class=\\"o\\">==</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">length</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">length</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Limit '%d' more upper than buffer size '%d' bytes\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">length</span><span class=\\"p\\">,</span><span class=\\"n\\">_size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">length</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">length</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"22封装-binarywriter\\">2.2、封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">flush</code>:封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">flush</code>,不过 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">flush</code> 实际上是从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter</code> 继承而来的。其作用是「Flushes the underlying stream」。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">flush</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_pOther</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_pOther</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">_pOther</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"23构造函数拷贝构造函数和析构函数\\">2.3、构造函数、拷贝构造函数和析构函数</h6>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pOther</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_size</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"c1\\">// Consctruction by copy</span>\\n<span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_pOther</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_size</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>注意析构函数中会进行 <code class=\\"language-plaintext highlighter-rouge\\">flush</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::~</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"四amfreader\\">四、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code></h3>\\n\\n<h4 id=\\"1objectdef\\">1、<code class=\\"language-plaintext highlighter-rouge\\">ObjectDef</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ObjectDef</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span> \\n <span class=\\"n\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">(</span><span class=\\"n\\">amf3</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">dynamic</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">externalizable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">count</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">arrayType</span><span class=\\"p\\">(</span><span class=\\"n\\">arrayType</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">string</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">hardProperties</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reset</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">dynamic</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">externalizable</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">count</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2amfreader-定义\\">2、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 定义</h4>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 作为其成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">AMFReader</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">AMFReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readSimpleObject</span><span class=\\"p\\">(</span><span class=\\"n\\">AMFSimpleObject</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">object</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">read</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">readNumber</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">readInteger</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readBoolean</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">readByteArray</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Timestamp</span> <span class=\\"n\\">readDate</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readObject</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readArray</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readDictionary</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">weakKeys</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readKey</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readItem</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">readRawObjectContent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readNull</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">startReferencing</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">stopReferencing</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">;</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*&gt;</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_stringReferences</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_classDefReferences</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_amf0References</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_amf0Reset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_reset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_amf3</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">_referencing</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数析构函数\\">2.1、构造函数、析构函数</h5>\\n\\n<p>参数为 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code>,会初始化一些成员变量。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">reader</span><span class=\\"p\\">(</span><span class=\\"n\\">reader</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_amf3</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_amf0Reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_referencing</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构时,会逐一释放 <code class=\\"language-plaintext highlighter-rouge\\">_objectDefs</code> 中对象的内存:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMFReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*&gt;::</span><span class=\\"n\\">iterator</span> <span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">delete</span> <span class=\\"o\\">*</span><span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22简单封装-packetreader-的一些函数\\">2.2、简单封装 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code>:操作指针位置</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_reset</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code>:根据当前缓冲区大小和 <code class=\\"language-plaintext highlighter-rouge\\">written</code> 计算得到</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">current</code>:<code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 内存地址</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">*</span><span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23设置-gptr-位置\\">2.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 位置</h5>\\n\\n<p>其实 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 也被影响了,但是在 <code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 中只用 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>。调用构造函数的时候,<code class=\\"language-plaintext highlighter-rouge\\">reset</code> 被设为 0,其后在每次读取数据的时候都会影响 <code class=\\"language-plaintext highlighter-rouge\\">reset</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_reset</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24判断类型\\">2.4、判断类型</h5>\\n\\n<p>分析请看注释:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">followingType</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先 <code class=\\"language-plaintext highlighter-rouge\\">reset</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">back</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">amf3</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>是 AMF0 类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果没有可读数据了,则返回 AMF::End。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">available</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">End</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>开始读了,先读到的表示 AMF 数据类型。要注意的是调用 current 并不改变指针的位置,所以你会在线面看到调用 next。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt8</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_amf3</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF_AVMPLUS_OBJECT</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">available</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">End</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>AMF3 类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>Undefined 和 null 都当做 null。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_UNDEFINED</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_NULL</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>false 和 true 都是 boolean。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_FALSE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_TRUE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_INTEGER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Integer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_NUMBER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">String</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_DATE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Array</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_DICTIONARY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Dictionary</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Object</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_BYTEARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">ByteArray</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>落到 default 手里的话,就跳过这个字节,读取下一个。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unknown AMF3 type %.2x\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>AMF0 类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">switch</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">null</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">null</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_UNDEFINED</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_NULL</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BOOLEAN</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_NUMBER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">long string</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">string</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">string</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_LONG_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">String</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">mixed array</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">strict array</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">array</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_MIXED_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_STRICT_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Array</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_DATE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin object</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">begin typed object</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">object</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BEGIN_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BEGIN_TYPED_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Object</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是引用,就跳过表示类型值的这个字节。这个先留下来,带我们分析完 readArray 和 readObject 再回头看。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_REFERENCE</span><span class=\\"p\\">:</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read16</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">reference</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_amf0References</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF0 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_amf0Reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_amf0References</span><span class=\\"p\\">[</span><span class=\\"n\\">reference</span><span class=\\"p\\">]);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果没了,或者不支持,或者都不是,就跳过这个字节,递归继续读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_END_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF end object type without begin object type before\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_UNSUPPORTED</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unsupported type in AMF format\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">default</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unknown AMF type %.2x\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">followingType</code> 是这个类的核心,每个具体的数据类型的分析都依赖于它的判断。这些类型的解析,会在下一篇文章中介绍。</p>\\n\\n<h4 id=\\"3解析-as3-null\\">3、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Null</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readNull</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先 reset 一下是惯例,就像糗百上的割一样。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span> \\n</code></pre></div></div>\\n\\n<p>AMF 数据类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">Null</code>,跳过该字节,并返回</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>判断错误</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Null type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4解析-as3-number\\">4、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Number</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">double</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readNumber</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Null</code> 会被悲催的跳过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 呀 :(</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Number type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过该字节吧</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>返回吧,返回之前还用到 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 的运算符,注意 AS3 中的 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 就是 C++ 的 <code class=\\"language-plaintext highlighter-rouge\\">double</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5解析-as3-integer\\">5、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Int32</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readInteger</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code> 类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>Null 的话:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code> 或者 Number 的话。。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Integer</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Integer type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过吧。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>终于是 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读一个变长的 32 位无符号整数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Forced in AMF3 here!</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果大于 3.5 个字节所能表示的最大无符号整数值(<code class=\\"language-plaintext highlighter-rouge\\">268435455</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">0xFFFFFFF</code>),则减去 <code class=\\"language-plaintext highlighter-rouge\\">0x2FFFFFFF</code>(这还不是太理解,有能解释的朋友给留个言,或者发 email 给我 )</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">268435455</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">value</span> <span class=\\"o\\">-=</span> <span class=\\"p\\">(</span><span class=\\"mi\\">1</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"mi\\">29</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"6解析-as3-boolean\\">6、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readBoolean</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">Null</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>居然不是 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code> 的话。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Boolean type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 的话,返回 <code class=\\"language-plaintext highlighter-rouge\\">true</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">false</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span><span class=\\"o\\">==</span> <span class=\\"n\\">AMF3_FALSE</span> <span class=\\"o\\">?</span> <span class=\\"nb\\">false</span> <span class=\\"o\\">:</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">AMF0</code> 喽:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span><span class=\\"o\\">==</span><span class=\\"mh\\">0x00</span> <span class=\\"o\\">?</span> <span class=\\"nb\\">false</span> <span class=\\"o\\">:</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"7开始引用与结束引用\\">7、开始引用与结束引用</h4>\\n\\n<p>如下这两个函数会在 <code class=\\"language-plaintext highlighter-rouge\\">FlowConnection</code> 中调用。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">startReferencing</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_referencing</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">stopReferencing</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_referencing</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"8解析-as3-bytearray\\">8、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code></h4>\\n\\n<p>先回顾一下 AMF3 中的ByteArray 的数据格式:</p>\\n\\n<p>注意到,首先要读取一个变长无符号 32 位整数,但是最低位是 1,只有 28 位用于表示数据长度。解释完这里,下面的解析过程才好理解。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readByteArray</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Null</code> 就返回 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReaderNull</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>,也返回 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReaderNull</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">ByteArray</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF ByteArray type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过这个字节:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>注意 position 返回的是相对位置,与 AS3 中一样。<code class=\\"language-plaintext highlighter-rouge\\">reference</code> 表示这个地址(简单说,引用就是地址嘛)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>读取一个变长 32 位无符号整数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>最低位是 1 的话,<code class=\\"language-plaintext highlighter-rouge\\">isInline</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,否则为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>右移一位,因为那一位是标志位,上面解释过了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">isInline</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,表示是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">_referencing</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code> 的话(这是一个 <code class=\\"language-plaintext highlighter-rouge\\">vector</code>),push back this reference:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不符合 ByteArray 的格式定义的话:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">size</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>移动到这个 reference 的位置,<code class=\\"language-plaintext highlighter-rouge\\">_references[size]</code> 就是这个位置(相对)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">size</span><span class=\\"p\\">]);</span> <span class=\\"c1\\">// TODO size 作为索引,还没有完全理解</span>\\n</code></pre></div></div>\\n\\n<p>读取这个 reference 的 size 值给 size对象(注意 size 是这个函数传入的引用参数,其值可以被修改)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>把读取完 ByteArraty 的 PacketReader 返回:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>最后强调一点,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 的数据段最大长度为 228 -1 字节,约为 256 MB。</p>\\n\\n<h4 id=\\"9解析-as3-date\\">9、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Date</code></h4>\\n\\n<p>先看下 <code class=\\"language-plaintext highlighter-rouge\\">Date</code> 的数据格式:</p>\\n\\n<p>下面开始分析:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Timestamp</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readDate</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>Null 的话,就返回当前时间:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是 Date 类型,也返回当前时间:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Date type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是 AMF3:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先读取 flag,最低一位必须是 1,其他位丢到垃圾桶。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">flags</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>当前相对位置。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>是 1 就 push back 到 <code class=\\"language-plaintext highlighter-rouge\\">_references</code> 里。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">flags</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>读取一个 double,到 result 里(result 也是 double 类型哦~)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位不是 1,麻烦不少哒。。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flags</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果 flag 超了,就返回当前时间作为时间戳作为 Date。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">flags</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这段与 ByteArray 那段一样:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">flags</span><span class=\\"p\\">]);</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>返回喽~</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"nf\\">Timestamp</span><span class=\\"p\\">((</span><span class=\\"n\\">Timestamp</span><span class=\\"o\\">::</span><span class=\\"n\\">TimeVal</span><span class=\\"p\\">)</span> <span class=\\"n\\">result</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>读俩,因为是 double(64 位):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">2</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Timezone, useless</span>\\n</code></pre></div></div>\\n\\n<p>返回喽~</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"nf\\">Timestamp</span><span class=\\"p\\">((</span><span class=\\"n\\">Timestamp</span><span class=\\"o\\">::</span><span class=\\"n\\">TimeVal</span><span class=\\"p\\">)</span> <span class=\\"n\\">result</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"10解析-as3-dictionary\\">10、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Dictionary</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readDictionary</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">weakKeys</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>下面这段咱就略了。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Dictionary</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Dictionary type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过 type:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// AMF3</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// marker</span>\\n</code></pre></div></div>\\n\\n<p>当前相对位置值作为 <code class=\\"language-plaintext highlighter-rouge\\">reference</code>,再读个 <code class=\\"language-plaintext highlighter-rouge\\">size</code>,还是最低位必须为 1,不是就返回 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">size</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">isInline</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">size</span><span class=\\"o\\">&gt;</span><span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>下面要调用到 <code class=\\"language-plaintext highlighter-rouge\\">ObjectRef</code> 构造函数,这里再把其实现拿出来看看,其实主要是初始化了哪些成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">(</span><span class=\\"n\\">amf3</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">dynamic</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">externalizable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">count</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">arrayType</span><span class=\\"p\\">(</span><span class=\\"n\\">arrayType</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>可以看到要有一个 amf3,还有 <code class=\\"language-plaintext highlighter-rouge\\">reset</code> 置为 0,<code class=\\"language-plaintext highlighter-rouge\\">dynamic</code> 置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,<code class=\\"language-plaintext highlighter-rouge\\">externalizable</code> 也是 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,<code class=\\"language-plaintext highlighter-rouge\\">count</code> 是 0,<code class=\\"language-plaintext highlighter-rouge\\">arrayType</code> 成员要赋值。</p>\\n\\n<p>上面是插播哦,下面还要继续哒。创建这么一个对象,注意是 new 出来的,所以我们在《OpenRTMFP/Cumulus Primer(16)AMF解析之AMFReader》一文中提到了 AMFReader 的析构函数中要对 <code class=\\"language-plaintext highlighter-rouge\\">_objectRef</code> 的每个元素逐一析构的。<code class=\\"language-plaintext highlighter-rouge\\">arrayType</code> 就设置为 <code class=\\"language-plaintext highlighter-rouge\\">AMF3_DICTIONARY</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*</span> <span class=\\"n\\">pObjectDef</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nf\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">,</span> <span class=\\"n\\">AMF3_DICTIONARY</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">dynamic</span><span class=\\"o\\">=</span><span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pObjectDef</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位是 1,就直接 push back,跟之前一样。不过这里多了一个 <code class=\\"language-plaintext highlighter-rouge\\">pObjectDef</code>,所以还要设置一下它的计数为 <code class=\\"language-plaintext highlighter-rouge\\">size</code>,就是 <code class=\\"language-plaintext highlighter-rouge\\">dictionary</code> 数据大小。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位是 0,就把 <code class=\\"language-plaintext highlighter-rouge\\">count</code> 设置为下一个变长整数值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">size</span><span class=\\"p\\">]);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">*=</span> <span class=\\"mi\\">2</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>读一个字节,如果最小位是 1,weakKeys 就是 true,否则为 false。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">weakKeys</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">return</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T03:31:10+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 12668 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一流缓冲区\\" id=\\"markdown-toc-一流缓冲区\\">一、流缓冲区</a> <ul>\\n <li><a href=\\"#1了解-stdstreambuf\\" id=\\"markdown-toc-1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></a> <ul>\\n <li><a href=\\"#11单步移动内置指针\\" id=\\"markdown-toc-11单步移动内置指针\\">1.1、单步移动内置指针</a></li>\\n <li><a href=\\"#12获取-get-指针和-put-指针的位置\\" id=\\"markdown-toc-12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</a></li>\\n <li><a href=\\"#13设置-get-和-put-指针可达区域的上下界\\" id=\\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2memorystreambuf\\" id=\\"markdown-toc-2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></a> <ul>\\n <li><a href=\\"#21移动内置的-get-和-put-指针\\" id=\\"markdown-toc-21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</a></li>\\n <li><a href=\\"#22获取-get-和-put-指针当前位置\\" id=\\"markdown-toc-22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</a></li>\\n <li><a href=\\"#23获取缓冲区的起始位置和大小\\" id=\\"markdown-toc-23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</a></li>\\n <li><a href=\\"#24缓冲区的已写字节数\\" id=\\"markdown-toc-24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</a></li>\\n <li><a href=\\"#25显式设定-put-和-get-指针位置\\" id=\\"markdown-toc-25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</a></li>\\n <li><a href=\\"#26-修改缓冲区大小\\" id=\\"markdown-toc-26-修改缓冲区大小\\">2.6 修改缓冲区大小</a></li>\\n <li><a href=\\"#27构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二io-流\\" id=\\"markdown-toc-二io-流\\">二、IO 流</a> <ul>\\n <li><a href=\\"#1了解-stdios\\" id=\\"markdown-toc-1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></a></li>\\n <li><a href=\\"#2memoryios\\" id=\\"markdown-toc-2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></a> <ul>\\n <li><a href=\\"#21构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#22得到-memorystreambuf-成员的地址\\" id=\\"markdown-toc-22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</a></li>\\n <li><a href=\\"#23当前位置\\" id=\\"markdown-toc-23当前位置\\">2.3、当前位置</a></li>\\n <li><a href=\\"#24封装-memorystreambuf-成员的一些函数\\" id=\\"markdown-toc-24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</a></li>\\n <li><a href=\\"#25-缓冲区可读数据的字节数\\" id=\\"markdown-toc-25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3输入流\\" id=\\"markdown-toc-3输入流\\">3、输入流</a></li>\\n <li><a href=\\"#4输出流\\" id=\\"markdown-toc-4输出流\\">4、输出流</a> <ul>\\n <li><a href=\\"#41-构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#42-读取和设定已写字节数\\" id=\\"markdown-toc-42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</a></li>\\n <li><a href=\\"#43-当前位置\\" id=\\"markdown-toc-43-当前位置\\">4.3 当前位置</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#三局部内存片\\" id=\\"markdown-toc-三局部内存片\\">三、局部内存片</a> <ul>\\n <li><a href=\\"#1构造函数\\" id=\\"markdown-toc-1构造函数\\">1、构造函数</a></li>\\n <li><a href=\\"#2析构函数\\" id=\\"markdown-toc-2析构函数\\">2、析构函数</a></li>\\n <li><a href=\\"#3缓冲区切割\\" id=\\"markdown-toc-3缓冲区切割\\">3、缓冲区切割</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\\n\\n<h3 id=\\"一流缓冲区\\">一、流缓冲区</h3>\\n\\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\\n\\n<h4 id=\\"1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></h4>\\n\\n<p>首先要了解 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 内置了一个 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针和一个 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针。<code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\\"language-plaintext highlighter-rouge\\">g</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">p</code> 就分别表示 get pointer 和 put pointer。</p>\\n\\n<h5 id=\\"11单步移动内置指针\\">1.1、单步移动内置指针</h5>\\n\\n<p>Increase get pointer: Advances the get pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">gbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>Increase put pointer: Advances the put pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">pbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</h5>\\n\\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">gptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">pptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</h5>\\n\\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setg</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gnext</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setp</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<h4 id=\\"2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></h4>\\n\\n<p>类定义:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryStreamBuf</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">streambuf</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">friend</span> <span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span><span class=\\"p\\">;</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">overflow</span><span class=\\"p\\">(</span><span class=\\"n\\">int_type</span> <span class=\\"n\\">c</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">underflow</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">sync</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"k\\">operator</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\\n\\n<h5 id=\\"21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针都移动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">gbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</h5>\\n\\n<p>封装 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">gptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</h5>\\n\\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</h5>\\n\\n<p>读取(其中也可能发生设置操作):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// 已写字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">written</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_written</span><span class=\\"o\\">=</span><span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</h5>\\n\\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Save nb char written</span>\\n \\n <span class=\\"c1\\">// 移动 put 指针</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 移动 get 指针</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"26-修改缓冲区大小\\">2.6 修改缓冲区大小</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// 大小标识</span>\\n <span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// gptr 当前位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span> \\n <span class=\\"c1\\">// pptr 当前位置</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>构造函数会设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">bufferSize</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数会拷贝对方的 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code>、<code class=\\"language-plaintext highlighter-rouge\\">bufferSizse</code>、<code class=\\"language-plaintext highlighter-rouge\\">_written</code>,并设定 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>、<code class=\\"language-plaintext highlighter-rouge\\">pptr</code>。注意设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 时,要分别调用 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pbump</code>,因为 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 仅将 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">(),</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">));</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二io-流\\">二、IO 流</h3>\\n\\n<h4 id=\\"1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></h4>\\n\\n<p>Initialize object [<code class=\\"language-plaintext highlighter-rouge\\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">init</span> <span class=\\"p\\">(</span> <span class=\\"n\\">streambuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">sb</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>初始化后如下函数的返回值:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>member function</th>\\n <th>value</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>rdbuf()</td>\\n <td>sb</td>\\n </tr>\\n <tr>\\n <td>tie()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>rdstate()</td>\\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\\n </tr>\\n <tr>\\n <td>exceptions()</td>\\n <td>goodbit</td>\\n </tr>\\n <tr>\\n <td>flags()</td>\\n <td>skipws | dec</td>\\n </tr>\\n <tr>\\n <td>width()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>precision()</td>\\n <td>6</td>\\n </tr>\\n <tr>\\n <td>fill()</td>\\n <td>‘ ’ (whitespace)</td>\\n </tr>\\n <tr>\\n <td>getloc()</td>\\n <td>a copy of locale()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code>,且是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryIOS</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"k\\">virtual</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ios</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span> <span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">poco_ios_init</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">init</code> 的宏定义,用于初始化成员 <code class=\\"language-plaintext highlighter-rouge\\">_buf</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_buf</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23当前位置\\">2.3、当前位置</h5>\\n\\n<p>这是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutpuStream</code> 继承时实现:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code> 封装为 <code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"p\\">(</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">());</span> <span class=\\"c1\\">// 缓冲区剩余可读数据字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">result</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3输入流\\">3、输入流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryInputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryInputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for reading.</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 以及 <code class=\\"language-plaintext highlighter-rouge\\">istream</code>。<code class=\\"language-plaintext highlighter-rouge\\">istream</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">iostream</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">basic_istream</code> 别名(<code class=\\"language-plaintext highlighter-rouge\\">typedef</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"k\\">const_cast</span><span class=\\"o\\">&lt;</span><span class=\\"kt\\">char</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>唯一的一个成员函数是 <code class=\\"language-plaintext highlighter-rouge\\">current</code>,封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的 <code class=\\"language-plaintext highlighter-rouge\\">gCurrent</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4输出流\\">4、输出流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryOutputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span>\\n <span class=\\"c1\\">/// An input stream for reading from a memory area.</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryOutputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for writing.</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>如下,不赘述了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</h5>\\n\\n<p>读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"43-当前位置\\">4.3 当前位置</h5>\\n\\n<p>与 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 中的封装类似:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三局部内存片\\">三、局部内存片</h3>\\n\\n<p>在第一部分的流缓冲区介绍 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 中有一个 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"1构造函数\\">1、构造函数</h4>\\n\\n<p>构造函数传入的参数对应的就是 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">(</span><span class=\\"n\\">offset</span><span class=\\"p\\">),</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">(</span><span class=\\"n\\">buffer</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&gt;=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2析构函数\\">2、析构函数</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3缓冲区切割\\">3、缓冲区切割</h4>\\n\\n<p>可以看到构造函数和析构函数中都调用了 <code class=\\"language-plaintext highlighter-rouge\\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\\n\\n<ul>\\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 获取到 gptr</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gpos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"c1\\">// 偏移缓冲区地址,并修改缓冲区大小</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">ppos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">gpos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">ppos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>一名出色软件工程师的技术基本功:编程与工具</title>\\n \\t<meta name=\\"description\\" content=\\"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>一名出色软件工程师的技术基本功:编程与工具</h2>\\t\\t\\n\\t<time datetime=\\"2012-05-15T17:06:59+00:00\\" class=\\"by-line\\">15 May 2012, 广州 | 作者 麦克船长 | 总计 2132 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"0写在前面\\">0、写在前面</h3>\\n\\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\\n\\n<h3 id=\\"1编程\\">1、编程</h3>\\n\\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\\n\\n<h4 id=\\"如果你精通c语言\\">如果你精通 C 语言</h4>\\n\\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\\n\\n<h4 id=\\"如果你精通c语言-1\\">如果你精通 C++ 语言</h4>\\n\\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\\n\\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\\n\\n<h4 id=\\"精通的关键还是针对语言核心来说的\\">精通的关键,还是针对语言核心来说的。</h4>\\n\\n<p>第一,你要对这个语言的语法特性熟稔;</p>\\n\\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\\n\\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\\n\\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\\n\\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\\n\\n<h4 id=\\"与计算机网络的基础结构相关联的技术实现\\">与计算机、网络的基础结构相关联的技术实现</h4>\\n\\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\\n\\n<h3 id=\\"2工具\\">2、工具</h3>\\n\\n<p>对于工具有三个层面:</p>\\n\\n<p>第一,是熟练的使用一些工具。</p>\\n\\n<p>第二,是能够发现提高生产力的工具。</p>\\n\\n<p>第三,是能够在无可用工具时自己编写工具。</p>\\n\\n<p>那么都有哪些最最最基本的工具呢?</p>\\n\\n<h4 id=\\"ideintegrateddevelopmentenvironment\\">IDE(Integrated Development Environment)</h4>\\n\\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\\n\\n<h4 id=\\"vcsversioncontrolsystem\\">VCS(Version Control System)</h4>\\n\\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\\n\\n<blockquote>\\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black &amp; white.</p>\\n</blockquote>\\n\\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\\n\\n<h4 id=\\"clicommandlineinterface\\">CLI(Command Line Interface)</h4>\\n\\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\\n\\n<h3 id=\\"3结语\\">3、结语</h3>\\n\\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-07T15:34:18+00:00\\" class=\\"by-line\\">07 Jun 2012, 广州 | 作者 麦克船长 | 总计 1538 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>OpenRTMFP/Cumulus 提供了 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>。</p>\\n\\n<p>一般来说,Thread A 会准备好要 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息。</p>\\n\\n<p>但是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>由于在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">createAMFMessage</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n \\n <span class=\\"c1\\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"p\\">(</span><span class=\\"n\\">_closed</span> <span class=\\"o\\">||</span> <span class=\\"n\\">signature</span><span class=\\"p\\">.</span><span class=\\"n\\">empty</span><span class=\\"p\\">()</span> <span class=\\"o\\">||</span> <span class=\\"n\\">_band</span><span class=\\"p\\">.</span><span class=\\"n\\">failed</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">MessageBuffered</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"n\\">_MessageNull</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后再调用时最后再增加 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Mutex</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedLock</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">msgQueueMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就基本解决了这个线程安全问题。</p>\\n\\n<p>另外,使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-25T02:56:26+00:00\\" class=\\"by-line\\">25 Jun 2012, 广州 | 作者 麦克船长 | 总计 2111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中的线程都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>,在其中封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 函数如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样一个类继承 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code>,然后调用到该类自己的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\\"language-plaintext highlighter-rouge\\">SocketManager</code> 为例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">SocketManager</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span> \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>我们要看看这个 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 是怎么回事,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">running</span><span class=\\"p\\">()</span> <span class=\\"k\\">const</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>很简单,就是通过 <code class=\\"language-plaintext highlighter-rouge\\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 是什么时候被设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 的呢?就是上面的 <code class=\\"language-plaintext highlighter-rouge\\">start()</code>,这里存在的一个问题就是先 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 线程,再设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_thread.start(_process);\\n_stop=false;\\n</code></pre></div></div>\\n\\n<p>而 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 之后 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 的时候就开始通过 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 来判断 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值了。所以你会在使用 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>它们是:</p>\\n\\n<ul>\\n <li>主线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程</li>\\n</ul>\\n\\n<p>而异常情况可能是 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动,甚至 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\\" alt=\\"image\\" /></p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动的情况 T.T</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\\n\\n<p>解决办法就是将 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span> \\n <span class=\\"c1\\">// June 25th, 2012, Michael@YY</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</title>\\n \\t<meta name=\\"description\\" content=\\"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</h2>\\t\\t\\n\\t<time datetime=\\"2012-07-23T03:07:43+00:00\\" class=\\"by-line\\">23 Jul 2012, 广州 | 作者 麦克船长 | 总计 3111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1客户端发布publishing-on-client-side\\" id=\\"markdown-toc-1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</a></li>\\n <li><a href=\\"#2服务器端server-side\\" id=\\"markdown-toc-2服务器端server-side\\">2、服务器端(Server-side)</a></li>\\n <li><a href=\\"#3客户端订阅subscribing-on-client-side\\" id=\\"markdown-toc-3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</a></li>\\n <li><a href=\\"#4reference\\" id=\\"markdown-toc-4reference\\">4、Reference</a></li>\\n</ul>\\n\\n<p>整个流程概括如下:</p>\\n\\n<p>Flash 客户端通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 建立连接,然后通过 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\\n\\n<h3 id=\\"1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</h3>\\n\\n<p>通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\\"/2012/04/10/openrtmfp-cumulus-1/\\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\\"language-plaintext highlighter-rouge\\">nc</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost:1935\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\\"language-plaintext highlighter-rouge\\">ns1</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">ns1</span><span class=\\"p\\">.</span><span class=\\"nx\\">publish</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"poechant_media_flow\\"</span><span class=\\"p\\">,</span> <span class=\\"s2\\">\\"live\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\\n\\n<h3 id=\\"2服务器端server-side\\">2、服务器端(Server-side)</h3>\\n\\n<p>Cumulus 通过 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPReceiving</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">rtmfpReceiving</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\\n\\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\\"language-plaintext highlighter-rouge\\">Handshake</code> 类的 <code class=\\"language-plaintext highlighter-rouge\\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ServerSession</span><span class=\\"o\\">::</span><span class=\\"n\\">packetHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Flow</span><span class=\\"o\\">::</span><span class=\\"n\\">fragmentSortedHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">stage</span><span class=\\"p\\">,</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">fragment</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">flags</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF_WITH_HANDLER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">messageHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">amf</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AUDIO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">audioHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">VIDEO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">videoHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">rawHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>接下来在 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushAudioPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushVideoPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushDataPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中的 <code class=\\"language-plaintext highlighter-rouge\\">_listeners</code> 就是该 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Listener</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">addListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">writer</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">unbuffered</span><span class=\\"p\\">);</span>\\n \\n<span class=\\"kt\\">void</span> <span class=\\"nf\\">removeListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这两个函数来实现的。</p>\\n\\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\\n\\n<h3 id=\\"3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</h3>\\n\\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>ns2.play(\\"poechant_media_flow\\");\\n</code></pre></div></div>\\n\\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\\"language-plaintext highlighter-rouge\\">NetStream::send(…)</code> 的,用于发送 <code class=\\"language-plaintext highlighter-rouge\\">Data</code>,<code class=\\"language-plaintext highlighter-rouge\\">Audio</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Video</code> 的程序可以参考该例修改。</p>\\n\\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 初始化的时候,传入 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\\n\\n<h3 id=\\"4reference\\">4、Reference</h3>\\n\\n<ul>\\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\\n</ul>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-08-04T17:58:17+00:00\\" class=\\"by-line\\">04 Aug 2012, 广州 | 作者 麦克船长 | 总计 5236 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfpserver-线程的启动和等待\\" id=\\"markdown-toc-一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</a> <ul>\\n <li><a href=\\"#1pocothread\\" id=\\"markdown-toc-1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></a></li>\\n <li><a href=\\"#2封装一个可运行线程的类\\" id=\\"markdown-toc-2封装一个可运行线程的类\\">2、封装一个可运行线程的类</a></li>\\n <li><a href=\\"#3启动-rtmfpserver-线程\\" id=\\"markdown-toc-3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</a></li>\\n <li><a href=\\"#4rtmfpserver-线程等待\\" id=\\"markdown-toc-4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二rtmfpmanager-对-rtmfpserver-的影响\\" id=\\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</a></li>\\n</ul>\\n\\n<h3 id=\\"一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</h3>\\n\\n<h4 id=\\"1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></h4>\\n\\n<p>Cumulus 大量使用了 <code class=\\"language-plaintext highlighter-rouge\\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PoechantRunnable</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// your codes</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">};</span>\\n \\n<span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">PoechantRunnable</span> <span class=\\"n\\">runnable</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Image that it's a gift</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">thread</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// And… thread is just like your girl</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">runnable</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Okay, give your sweet babe the gift :)</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2封装一个可运行线程的类\\">2、封装一个可运行线程的类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中实现了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 类,该类继承了 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,就是上面那个 <code class=\\"language-plaintext highlighter-rouge\\">gift</code> 喽。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">StartableProcess</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span><span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">);</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>可以看到其中有 <code class=\\"language-plaintext highlighter-rouge\\">Startable&amp; _startable</code> 引用成员,它并没有继承 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,而是封装了 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">_thread</span><span class=\\"p\\">;</span>\\n<span class=\\"n\\">StartableProcess</span> <span class=\\"n\\">_process</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>这里 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 封装了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 成员,与 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\\n\\n<h4 id=\\"3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</h4>\\n<p>我们可以看到在 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的构造函数中初始化了 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\\"language-plaintext highlighter-rouge\\">(Flag Field)_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,因为它还没有调用启动函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_name</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_stop</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_process</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>初始化 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 时,调用 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">StartableProcess</span><span class=\\"o\\">::</span><span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">(</span><span class=\\"n\\">startable</span><span class=\\"p\\">){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>传入 <code class=\\"language-plaintext highlighter-rouge\\">_startable</code> 的引用。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的,然后通过调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 来启动,启动后会响应到 <code class=\\"language-plaintext highlighter-rouge\\">run()</code>。下面我们以 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程为例。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 类是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPServer</span>\\n <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Gateway</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">protected</span> <span class=\\"n\\">Handler</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">SocketHandler</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">RTMFPServer</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">cores</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">(</span><span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>所在线程</th>\\n <th>调用者</th>\\n <th>函数</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>主线程</td>\\n <td>main(…)</td>\\n <td> </td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer从Startable继承来的Thread成员</td>\\n <td>Thread::start(…)</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\\n <td>StartableProcess::run()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::run()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</h4>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"n\\">terminate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 函数很简单,如下只进行了 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">giveHandle()</code> 两个操作。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">){</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">()</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">giveHandle</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的,声明如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">WakeUpType</span> <span class=\\"nf\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>在运行状态下,<code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,而默认参数 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code> 为 0,所以会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>这个 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员是一个 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 对象,<code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::wait()</code> 后,会一直等待 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\\"language-plaintext highlighter-rouge\\">wait</code> 的状态。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">set</code> 的动作是由:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::requestHandle()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">PoolThread::push(Poco::AutoPtr&lt;RunnableType&gt;&amp; pRunnable)</code></li>\\n</ul>\\n\\n<p>执行的。</p>\\n\\n<h3 id=\\"二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 同样,继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPManager</span> <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Task</span><span class=\\"p\\">,</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span>\\n</code></pre></div></div>\\n\\n<p>在构造函数中将 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\\"language-plaintext highlighter-rouge\\">_server</code> 引用成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPManager</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">server</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_server</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Task</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPManager\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"cm\\">/* ...... */</span>\\n\\n<span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_server</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的构造函数中调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 成员函数,是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的线程。然后响应到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager::run()</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">PRIO_LOW</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">2000</span><span class=\\"p\\">)</span><span class=\\"o\\">!=</span><span class=\\"n\\">STOP</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">waitHandle</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里要强调的是,这里的 <code class=\\"language-plaintext highlighter-rouge\\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\\n\\n<p>在这里我们可以看到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的主循环的等待时间的。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>你可以自行修改 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 的参数,这样就会调用 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code>)来进行睡眠。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\\"language-plaintext highlighter-rouge\\">handle</code> 成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handle</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_server</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里就会调用到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 源码时知道 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code> 函数并不是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程内运行的,而是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">manage</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\\"language-plaintext highlighter-rouge\\">Session</code>。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>我们是应该「断舍离」还是「念念不忘,必有回响」</title>\\n \\t<meta name=\\"description\\" content=\\"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>我们是应该「断舍离」还是「念念不忘,必有回响」</h2>\\t\\t\\n\\t<time datetime=\\"2017-01-31T20:59:21+00:00\\" class=\\"by-line\\">31 Jan 2017, 北京 | 作者 麦克船长 | 总计 2577 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"引子\\">引子</h3>\\n\\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\\n\\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\\n\\n<p>「嗨,我说老朱,讲讲你啊!」</p>\\n\\n<p>「我就不提啦。」</p>\\n\\n<p>「为什么啊?说说,说说!」</p>\\n\\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\\n\\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\\n\\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\\n\\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\\n\\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\\n\\n<blockquote>\\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\\n</blockquote>\\n\\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\\n\\n<h3 id=\\"断舍离\\">断舍离</h3>\\n\\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\\" alt=\\"image\\" /></p>\\n\\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\\n\\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\\n\\n<h3 id=\\"念念不忘必有回响\\">念念不忘,必有回响</h3>\\n\\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\\" alt=\\"image\\" /></p>\\n\\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\\n\\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\\n\\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\\n\\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\\n\\n<h3 id=\\"用正负反馈来判断何时何为\\">用「正负反馈」来判断何时何为?</h3>\\n\\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\\n\\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\\n\\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\\n\\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\\n\\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\\n\\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\\n\\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\\n\\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\\n\\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\\n\\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\\n\\n<p>然后,我们一起等待,那声回响。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>未来人工智能就是要:让普通人过上现在富豪们的生活</title>\\n \\t<meta name=\\"description\\" content=\\"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>未来人工智能就是要:让普通人过上现在富豪们的生活</h2>\\t\\t\\n\\t<time datetime=\\"2017-02-23T18:23:33+00:00\\" class=\\"by-line\\">23 Feb 2017, 北京 | 作者 麦克船长 | 总计 627 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\\n\\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\\n\\n<h4 id=\\"但是我没有钱呢\\">但是我没有钱呢?</h4>\\n\\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\\n\\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\\n\\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\\n\\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\\n\\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\\n\\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\\n\\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>延迟满足,才有自由</title>\\n \\t<meta name=\\"description\\" content=\\"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>延迟满足,才有自由</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-11T06:18:03+00:00\\" class=\\"by-line\\">11 Apr 2020, 杭州 | 作者 麦克船长 | 总计 4478 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\\n\\n<h3 id=\\"1儿童时期的延迟满足\\">1、儿童时期的延迟满足</h3>\\n\\n<h4 id=\\"棉花糖实验\\">棉花糖实验</h4>\\n\\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\\n\\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\\n\\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\\n\\n<p>先讲两个我自己的例子吧。</p>\\n\\n<h4 id=\\"黑加仑零食\\">黑加仑零食</h4>\\n\\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\\n\\n<h4 id=\\"暑假作业\\">暑假作业</h4>\\n\\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\\n\\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\\n\\n<h4 id=\\"延迟满足的背后是意志力自控力\\">延迟满足的背后,是意志力/自控力</h4>\\n\\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\\n\\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\\n\\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\\n\\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\\n\\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\\n\\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\\n\\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\\n\\n<h3 id=\\"2快乐理论挑战延迟满足\\">2、快乐理论,挑战延迟满足</h3>\\n\\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\\n\\n<h4 id=\\"烂苹果\\">烂苹果</h4>\\n\\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\\n\\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\\n\\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\\n\\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\\n\\n<h4 id=\\"快乐理论\\">快乐理论</h4>\\n\\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\\n\\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\\n\\n<p>但这个理论角度对吗?</p>\\n\\n<h3 id=\\"3我们应该在哪个范畴内讨论延迟满足\\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\\n\\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\\n\\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\\n\\n<h4 id=\\"仅有正反馈刺激的奶头乐\\">仅有正反馈刺激的奶头乐</h4>\\n\\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\\n\\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\\n\\n<h4 id=\\"延迟满足重在顺序而非时间\\">延迟满足重在顺序,而非时间</h4>\\n\\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\\n\\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\\n\\n<h3 id=\\"4常见的延迟满足与即时满足\\">4、常见的延迟满足与即时满足</h3>\\n\\n<h4 id=\\"初阶对手vs浅度延迟满足\\">初阶对手 vs 浅度延迟满足</h4>\\n\\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\\n\\n<ul>\\n <li>\\n <p>睡懒觉</p>\\n </li>\\n <li>\\n <p>过量饮食</p>\\n </li>\\n <li>\\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\\n </li>\\n <li>\\n <p>冲动情绪</p>\\n </li>\\n <li>\\n <p>炫耀(粗俗说就是装B)</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\\n\\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\\n\\n<h4 id=\\"中阶对手vs中度延迟满足\\">中阶对手 vs 中度延迟满足</h4>\\n\\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\\n\\n<ul>\\n <li>\\n <p>在家边看电视/视频,边学习/工作</p>\\n </li>\\n <li>\\n <p>依赖二手知识,而怠于一手知识</p>\\n </li>\\n <li>\\n <p>刷知乎、行业资讯 APP</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>例子会有很多,我们仅稍微说下这三个。</p>\\n\\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\\n\\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\\n\\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\\n\\n<h4 id=\\"高阶对手vs深度延迟满足\\">高阶对手 vs 深度延迟满足</h4>\\n\\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\\n\\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\\n\\n<p>我这里就说一个影响很大的:</p>\\n\\n<p>* 急于行动</p>\\n\\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\\n\\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\\n\\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\\n\\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\\n\\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\\n\\n<h3 id=\\"5延迟满足的驻留时长\\">5、延迟满足的驻留时长</h3>\\n\\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\\n\\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\\n\\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\\n\\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\\n\\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\\n\\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\\n\\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\\n\\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>疫后怎么做餐饮品牌?三叉戟模式或成标配</title>\\n \\t<meta name=\\"description\\" content=\\"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>疫后怎么做餐饮品牌?三叉戟模式或成标配</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-14T16:42:43+00:00\\" class=\\"by-line\\">14 Apr 2020, 杭州 | 作者 麦克船长 | 总计 1845 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\\n\\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\\n\\n<ul>\\n <li>外卖</li>\\n <li>做饭</li>\\n <li>速食</li>\\n</ul>\\n\\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\\n\\n<h3 id=\\"标品化外卖业务\\">标品化外卖业务</h3>\\n\\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\\n\\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\\n\\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\\n\\n<ul>\\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\\n</ul>\\n\\n<h3 id=\\"社区生鲜前置仓\\">社区生鲜前置仓</h3>\\n\\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\\n\\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\\n\\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\\n\\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\\n\\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\\n\\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\\n</ul>\\n\\n<h3 id=\\"线上预包装食品\\">线上预包装食品</h3>\\n\\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\\n\\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\\n\\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\\n</ul>\\n\\n<h3 id=\\"线下重构新餐饮时代到来\\">线下重构,新餐饮时代到来</h3>\\n\\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\\n\\n<ul>\\n <li>用「标品化外卖」覆盖外卖场景</li>\\n <li>用「生鲜前置仓」覆盖做饭场景</li>\\n <li>用「预包装食品」覆盖速食场景</li>\\n</ul>\\n\\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\\n\\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\\n\\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>担任淘宝产品总负责人的双十一,是怎样的体验?</title>\\n \\t<meta name=\\"description\\" content=\\"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>担任淘宝产品总负责人的双十一,是怎样的体验?</h2>\\t\\t\\n\\t<time datetime=\\"2020-11-11T15:59:43+00:00\\" class=\\"by-line\\">11 Nov 2020, 杭州 | 作者 麦克船长 | 总计 138 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\\n\\n<p><img src=\\"/img/src/2020-11-11-captain-double-eleven-1.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-2.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-3.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-4.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-5.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-6.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-7.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-8.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-9.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-10.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-11.jpg\\" alt=\\"image\\" /></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的阿里一年香(入职阿里一周年)</title>\\n \\t<meta name=\\"description\\" content=\\"本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的阿里一年香(入职阿里一周年)</h2>\\t\\t\\n\\t<time datetime=\\"2021-06-04T15:42:43+00:00\\" class=\\"by-line\\">04 Jun 2021, 杭州 | 作者 麦克船长 | 总计 304 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>To 钟超</p>\\n\\n<p>1 周年快乐!很开心我们有这样一段共事的机会,虽开始时有些许波折,但随着进一步相处,我们很快能做到彼此欣赏、英雄相惜、默契配合,也特别感谢你对我的信任和支持,这是一切共事的基础。你强大的自驱力、脑力、对新事物的理解学习能力,都是最近几手新人里比较突出的。特别钦佩于你的执着和初性,对一件事认定后,迸发出的强大战斗力和决心。今天特卖这个新业务需要扎下根基,还真的需要一些舍我其谁的胆魄和更为犀利的突破,我也相信「新特卖」能成为你在阿里又一代表作,我希望我们的团队能为之骄傲和自豪,我们能不负公司所托,真正在下沉市场这场硬仗上有所建树,井取得令我们自己感到骄傲的突破,一起加油。</p>\\n\\n<p>From 麦克船长的主管</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</title>\\n \\t<meta name=\\"description\\" content=\\"阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</h2>\\t\\t\\n\\t<time datetime=\\"2021-11-11T19:59:43+00:00\\" class=\\"by-line\\">11 Nov 2021, 杭州 | 作者 麦克船长 | 总计 917 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2021-11-11-captain-tttm-1.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h3 id=\\"天天特卖团队理念\\">天天特卖团队理念</h3>\\n\\n<h4 id=\\"特卖合伙人\\">特卖合伙人</h4>\\n\\n<p>以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。特卖核心管理团队每 Q 会进行一次班子建设通晒。</p>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-9.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h4 id=\\"如何理解协作\\">如何理解协作?</h4>\\n\\n<p>从长时间线来看,我们是为了不断积累信用,像一张信用卡一样,不断获得别人愿意支持我们的更大额度。不要事情结果还可以,而我们却没有积累到信用。互联网本质也是现代工业。而现代工业,一是社会分工,二是社会协作。想取得现代工业项目的结果,就要有更大的人才包容度、环境包容度。工作的结果就是在妥协与博弈中取得的,这是和光同尘的本质,也是现代工业复杂系统拿到结果的本质。只有这样我们才能让越来越多的人追随我们一起 do something,这种追随不一定只有上下级才是,而是愿意并且相信和我们能到达更远的地方。这背后的信用,要我们一步一个脚印地去积累,对他人给予的信任要保持敬畏、如履薄冰、懂得感恩。对每一段阶段性或长或短结束的合作,都要表达感谢。</p>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-8.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h4 id=\\"如何看待同学的优势及短板\\">如何看待同学的优势及短板?</h4>\\n\\n<ul>\\n <li>优势:讲优势有两个可能的目的,要么组织会在未来任务分配上重点考虑发挥该同学优势的事情,要告诉 TA,要激励 TA,是 TA 前行的自信来源之一。要么是对于同学也把握不准的特点,我们明确告诉 TA 这是你被我欣赏的优点。</li>\\n <li>短板:什么是要讲的短板?未来一段时间,最期待你补足提升的。一旦这方面显著进步,就会向上迈进很大一步,甚至可以突破自己当下成长的瓶颈。要花多少篇幅讲?要比优势,有更大篇幅去讲。讲完就结束了么?对这个短板,一定要表达态度,也一定要对是否有方法、什么方法来补足短板要和同学沟通。</li>\\n <li>无论是优势,还是短板,要说到点儿上,不要说片儿汤话。要让同学们能够引起思考、启发的。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-10.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h3 id=\\"天天特卖期待你的加入\\">天天特卖期待你的加入!</h3>\\n\\n<p>新天天特卖缘起于「手淘下沉市场战役)」,于 2021 年初上线,以「极致性价比货源、裸价直降、全网比价、买贵必赔」打造手淘极致价格敏感人群的购物阵地。目前天天特卖团队有行业运营、用户运营、数据策略、整合营销、直播运营、内容运营等岗位,有兴趣的同学可以钉钉随时找我,期待你的加入!</p>\\n\\n<h4 id=\\"欢迎添加我的微信sinosuperman-推荐自荐--\\">欢迎添加我的微信:sinosuperman 推荐、自荐 ^ ^</h4>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-11.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-2.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-3.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-4.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-5.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-6.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-7.jpg\\" alt=\\"imagee\\" /></p>\\n\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\\n \\t<meta name=\\"description\\" content=\\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-21T15:53:57+00:00\\" class=\\"by-line\\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#写在前面\\" id=\\"markdown-toc-写在前面\\">写在前面</a></li>\\n <li><a href=\\"#1github上的准备\\" id=\\"markdown-toc-1github上的准备\\">1、GitHub 上的准备</a></li>\\n <li><a href=\\"#2了解ruby和jekyll\\" id=\\"markdown-toc-2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</a></li>\\n <li><a href=\\"#3了解gem\\" id=\\"markdown-toc-3了解gem\\">3、了解 Gem</a></li>\\n <li><a href=\\"#4安装homebrew\\" id=\\"markdown-toc-4安装homebrew\\">4、安装 Homebrew</a></li>\\n <li><a href=\\"#5用homebrew安装ruby\\" id=\\"markdown-toc-5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</a></li>\\n <li><a href=\\"#6安装jekyll和bundler\\" id=\\"markdown-toc-6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</a></li>\\n <li><a href=\\"#7使用bundle管理包依赖关系\\" id=\\"markdown-toc-7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</a></li>\\n <li><a href=\\"#8本地启动一下看看\\" id=\\"markdown-toc-8本地启动一下看看\\">8、本地启动一下看看</a></li>\\n <li><a href=\\"#9用jekyll创建一个项目\\" id=\\"markdown-toc-9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</a></li>\\n <li><a href=\\"#10修改gemfile文件\\" id=\\"markdown-toc-10修改gemfile文件\\">10、修改 Gemfile 文件</a></li>\\n <li><a href=\\"#11配置githubpages\\" id=\\"markdown-toc-11配置githubpages\\">11、配置 Github Pages</a></li>\\n <li><a href=\\"#12配置一个jekylltheme\\" id=\\"markdown-toc-12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</a></li>\\n <li><a href=\\"#13设置自定义域名\\" id=\\"markdown-toc-13设置自定义域名\\">13、设置自定义域名</a></li>\\n <li><a href=\\"#14用-rouge-实现代码高亮\\" id=\\"markdown-toc-14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</a></li>\\n <li><a href=\\"#15一些扩展问题\\" id=\\"markdown-toc-15一些扩展问题\\">15、一些扩展问题</a> <ul>\\n <li><a href=\\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\" id=\\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\\n <li><a href=\\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\" id=\\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\\n <li><a href=\\"#q3如何为每篇文章添加一个目录\\" id=\\"markdown-toc-q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</a></li>\\n <li><a href=\\"#q4如何在-jekyll-中支持-katex\\" id=\\"markdown-toc-q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\\n <li><a href=\\"#在-githubio-上\\" id=\\"markdown-toc-在-githubio-上\\">在 GitHub.io 上</a></li>\\n <li><a href=\\"#如果不在-githubio-上则还需要额外工作\\" id=\\"markdown-toc-如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\\n <li><a href=\\"#使用示例\\" id=\\"markdown-toc-使用示例\\">使用示例</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#q5jekyll-中如何支持-graphviz-\\" id=\\"markdown-toc-q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\\n <li><a href=\\"#q6如何显示--或者--\\" id=\\"markdown-toc-q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#参考\\" id=\\"markdown-toc-参考\\">参考</a></li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\\n\\n<ul>\\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\\n</ul>\\n\\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\\n\\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\\n\\n<h3 id=\\"1github上的准备\\">1、GitHub 上的准备</h3>\\n\\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git clone https://github.com/username/username.github.io\\n</code></pre></div></div>\\n\\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git add <span class=\\"nt\\">--all</span>\\n<span class=\\"nv\\">$ </span>git commit <span class=\\"nt\\">-m</span> <span class=\\"s2\\">\\"Initial commit\\"</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</h3>\\n\\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\\n\\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\\n\\n<h3 id=\\"3了解gem\\">3、了解 Gem</h3>\\n\\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\\n\\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\\ngem sources -l\\n</code></pre></div></div>\\n\\n<h3 id=\\"4安装homebrew\\">4、安装 Homebrew</h3>\\n\\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>/bin/bash <span class=\\"nt\\">-c</span> <span class=\\"s2\\">\\"</span><span class=\\"si\\">$(</span>curl <span class=\\"nt\\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\\"si\\">)</span><span class=\\"s2\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\\n\\n<h3 id=\\"5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</h3>\\n\\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>brew <span class=\\"nb\\">install </span>chruby ruby-install xz\\n</code></pre></div></div>\\n\\n<p>安装 Ruby 的最新版本:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby-install ruby\\n</code></pre></div></div>\\n\\n<p>这时候提示如下问题:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"o\\">&gt;&gt;&gt;</span> Updating ruby versions ...\\n<span class=\\"o\\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\\"se\\">\\\\</span>\\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\\n<span class=\\"o\\">!!!</span> Failed to download ruby versions!\\n</code></pre></div></div>\\n\\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ echo \\"185.199.111.133 raw.githubusercontent.com\\" &gt;&gt; /etc/hosts\\n</code></pre></div></div>\\n\\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/chruby.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/auto.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"chruby ruby-3.1.2\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc <span class=\\"c\\"># run 'chruby' to see actual version</span>\\n</code></pre></div></div>\\n\\n<p>再看下 Ruby 版本对不对:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby <span class=\\"nt\\">-v</span>\\n</code></pre></div></div>\\n\\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\\n\\n<h3 id=\\"6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"nb\\">install </span>jekyll bundler\\n</code></pre></div></div>\\n\\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\\n\\n<h3 id=\\"7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</h3>\\n\\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">source</span> <span class=\\"s1\\">'https://rubygems.org'</span>\\ngem <span class=\\"s1\\">'nokogiri'</span>\\ngem <span class=\\"s1\\">'rack'</span>, <span class=\\"s1\\">'~&gt; 2.2.4'</span>\\ngem <span class=\\"s1\\">'rspec'</span>\\ngem <span class=\\"s1\\">'jekyll'</span>\\n</code></pre></div></div>\\n\\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ git add Gemfile Gemfile.lock\\n</code></pre></div></div>\\n\\n<h3 id=\\"8本地启动一下看看\\">8、本地启动一下看看</h3>\\n\\n<p>先用 bundle 如下命令来启动:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: none\\n Source: /Users/captain/Workspace/poechant.github.io\\n Destination: /Users/captain/Workspace/poechant.github.io/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n done in 0.014 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\\n Server address: http://127.0.0.1:4000\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git pull <span class=\\"nt\\">--no-rebase</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll new CaptainMikeBlog\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>CaptainMikeBlog\\n<span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n Jekyll Feed: Generating feed for posts\\n done in 0.365 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\\n Server address: http://127.0.0.1:4000/\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"10修改gemfile文件\\">10、修改 Gemfile 文件</h3>\\n\\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"s2\\">\\"github-pages\\"</span>, <span class=\\"s2\\">\\"~&gt; GITHUB-PAGES-VERSION\\"</span>, group: :jekyll_plugins\\n</code></pre></div></div>\\n\\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ bundle install\\n</code></pre></div></div>\\n\\n<p>再本地启动服务器测试:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>得到如下提示:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\\nPrepending <span class=\\"sb\\">`</span>bundle <span class=\\"nb\\">exec</span><span class=\\"sb\\">`</span> to your <span class=\\"nb\\">command </span>may solve this. <span class=\\"o\\">(</span>Gem::LoadError<span class=\\"o\\">)</span>\\n</code></pre></div></div>\\n\\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle add webrick\\n<span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\\n\\n<h3 id=\\"11配置githubpages\\">11、配置 Github Pages</h3>\\n\\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>baseurl: \\"\\"\\nurl: \\"http://your-username.github.io\\"\\n</code></pre></div></div>\\n\\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\\n\\n<h3 id=\\"12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</h3>\\n\\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_includes\\n_layouts\\n_sass\\ncss\\njs\\nimg\\n404.markdown\\nindex.html\\n</code></pre></div></div>\\n\\n<p>不同主题会有所不同,这里只列个大概。</p>\\n\\n<h3 id=\\"13设置自定义域名\\">13、设置自定义域名</h3>\\n\\n<p>添加四条 A 记录,记录值如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>185.199.108.153\\n185.199.109.153\\n185.199.110.153\\n185.199.111.153\\n</code></pre></div></div>\\n\\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\\n\\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\\n\\n<h3 id=\\"14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</h3>\\n\\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem install kramdom rouge\\n</code></pre></div></div>\\n\\n<p>然后配置 _config.yml 文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>然后用 rouge 创建 syntax.css 文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>rougify style github <span class=\\"o\\">&gt;</span> css/syntax.css\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">_include/head.html</code> 文件中添加:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/css/syntax.css\\"</span> <span class=\\"nt\\">/&gt;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"15一些扩展问题\\">15、一些扩展问题</h3>\\n\\n<h4 id=\\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\\n\\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是一篇文章</span>\\n<span class=\\"na\\">excerpt</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是文章的摘要</span>\\n<span class=\\"nn\\">---</span>\\n\\n这是文章的正文内容\\n</code></pre></div></div>\\n\\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;ul&gt;</span>\\n {% for post in paginator.posts %}\\n <span class=\\"nt\\">&lt;li&gt;</span>\\n <span class=\\"nt\\">&lt;h2&gt;&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{{ post.url }}\\"</span><span class=\\"nt\\">&gt;</span>{{ post.title }}<span class=\\"nt\\">&lt;/a&gt;&lt;/h2&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>{{ post.excerpt }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n <span class=\\"nt\\">&lt;/li&gt;</span>\\n {% endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\\n\\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\\n\\n<h4 id=\\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\\n\\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code>目录下创建一个<code class=\\"language-plaintext highlighter-rouge\\">category.html</code>文件:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>---\\nlayout: default\\n---\\n\\n<span class=\\"nt\\">&lt;div</span> <span class=\\"na\\">class=</span><span class=\\"s\\">\\"container\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n {% if site.categories[page.category] %}\\n {% for post in site.categories[page.category] %}\\n <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{% if site.baseurl == \\"</span><span class=\\"err\\">/\\"</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">else</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">|</span> <span class=\\"na\\">prepend:</span> <span class=\\"na\\">site.baseurl</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">endif</span> <span class=\\"err\\">%}\\"</span><span class=\\"nt\\">&gt;</span>\\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\\n <span class=\\"nt\\">&lt;/a&gt;</span>\\n {% endfor %}\\n {% else %}\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>No posts for this category. If you have something in mind, check <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/write\\"</span><span class=\\"nt\\">&gt;</span>Write For Us<span class=\\"nt\\">&lt;/a&gt;</span>page.<span class=\\"nt\\">&lt;/p&gt;</span>\\n {% endif %}\\n<span class=\\"nt\\">&lt;/div&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">include</span><span class=\\"pi\\">:</span> <span class=\\"pi\\">[</span><span class=\\"s1\\">'</span><span class=\\"s\\">_categories'</span><span class=\\"pi\\">]</span>\\n</code></pre></div></div>\\n\\n<p>在根目录创建一个<code class=\\"language-plaintext highlighter-rouge\\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\\"language-plaintext highlighter-rouge\\">ai.html</code>如下:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">category</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">人工智能</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s\\">This is the description.</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/category/ai</span>\\n<span class=\\"na\\">category</span><span class=\\"pi\\">:</span> <span class=\\"s\\">ai</span>\\n<span class=\\"na\\">category_type</span><span class=\\"pi\\">:</span> <span class=\\"s\\">tech</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>那么之后每次创建文件时,在头部写<code class=\\"language-plaintext highlighter-rouge\\">category</code>一定要与这些<code class=\\"language-plaintext highlighter-rouge\\">categories</code>中的<code class=\\"language-plaintext highlighter-rouge\\">html</code>文件对应起来。</p>\\n\\n<h4 id=\\"q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</h4>\\n\\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">*</span> TOC\\n{:toc}\\n</code></pre></div></div>\\n\\n<h4 id=\\"q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\\n\\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\\n\\n<h5 id=\\"在-githubio-上\\">在 GitHub.io 上</h5>\\n\\n<p>先修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">math_engine</span><span class=\\"pi\\">:</span> <span class=\\"s\\">katex</span>\\n</code></pre></div></div>\\n\\n<p>然后修改 <code class=\\"language-plaintext highlighter-rouge\\">_includes/head.html</code> 文件,在 <code class=\\"language-plaintext highlighter-rouge\\">&lt;head&gt;</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">&lt;/head&gt;</code> 中间:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\">&lt;!--KaTeX--&gt;</span>\\n <span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span>\\n <span class=\\"na\\">href=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script&gt;</span>\\n <span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">addEventListener</span><span class=\\"p\\">(</span><span class=\\"dl\\">\\"</span><span class=\\"s2\\">DOMContentLoaded</span><span class=\\"dl\\">\\"</span><span class=\\"p\\">,</span> <span class=\\"kd\\">function</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"nx\\">renderMathInElement</span><span class=\\"p\\">(</span><span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">body</span><span class=\\"p\\">,</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// ...options...</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"nt\\">&lt;/script&gt;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</h5>\\n\\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem <span class=\\"nb\\">install </span>kramdom-math-katex\\n\\ngem <span class=\\"nb\\">install </span>katex\\ngem <span class=\\"nb\\">install </span>execjs\\n\\ngem <span class=\\"nb\\">install </span>therubyracer\\ngem <span class=\\"nb\\">install </span>therubyrhino\\ngem <span class=\\"nb\\">install </span>duktape\\n</code></pre></div></div>\\n\\n<h5 id=\\"使用示例\\">使用示例</h5>\\n\\n<p>以如下方式输入输入如下内容:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}\\n$$ \\\\sum_{i=1}^{n} a_i $$\\n{% endraw %}\\n</code></pre></div></div>\\n\\n<p>就会得到一个数学公式:</p>\\n\\n\\\\[\\\\sum_{i=1}^{n} a_i\\\\]\\n\\n<h4 id=\\"q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\\n\\n<p>这要依赖 <code class=\\"language-plaintext highlighter-rouge\\">jekyll-graphviz-dot</code>,修改 <code class=\\"language-plaintext highlighter-rouge\\">Gemfile</code> 增加一句:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>group :jekyll_plugins <span class=\\"k\\">do\\n </span>gem <span class=\\"s2\\">\\"jekyll-graphviz-dot\\"</span>\\nend\\n</code></pre></div></div>\\n\\n<p>再修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 配置文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-graphviz</span>\\n</code></pre></div></div>\\n\\n<p>再在本地安装 graphviz,可以通过 <code class=\\"language-plaintext highlighter-rouge\\">conda install graphviz</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">brew install graphviz</code>。然后 <code class=\\"language-plaintext highlighter-rouge\\">bundle install</code> 再 <code class=\\"language-plaintext highlighter-rouge\\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\\n\\n<pre><code class=\\"language-graphviz\\">{% graph some graph title %}\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n{% endgraph %}\\n</code></pre>\\n\\n<p>如果看到如下效果,就说明你都配置成功了:</p>\\n\\n<div class=\\"graphviz-wrapper\\">\\n\\n<!-- Generated by graphviz version 2.43.0 (0)\\n -->\\n<!-- Title: G Pages: 1 -->\\n<svg role=\\"img\\" aria-label=\\"some graph title\\" width=\\"89pt\\" height=\\"188pt\\" viewBox=\\"0.00 0.00 89.00 188.00\\">\\n<title>some graph title</title>\\n<desc>\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n</desc>\\n\\n<g id=\\"graph0\\" class=\\"graph\\" transform=\\"scale(1 1) rotate(0) translate(4 184)\\">\\n<title>G</title>\\n<polygon fill=\\"white\\" stroke=\\"transparent\\" points=\\"-4,4 -4,-184 85,-184 85,4 -4,4\\" />\\n<!-- a -->\\n<g id=\\"node1\\" class=\\"node\\">\\n<title>a</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-162\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-158.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">a</text>\\n</g>\\n<!-- b -->\\n<g id=\\"node2\\" class=\\"node\\">\\n<title>b</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"27\\" cy=\\"-90\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"27\\" y=\\"-86.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">b</text>\\n</g>\\n<!-- a&#45;&gt;b -->\\n<g id=\\"edge1\\" class=\\"edge\\">\\n<title>a&#45;&gt;b</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\\" />\\n</g>\\n<!-- c -->\\n<g id=\\"node3\\" class=\\"node\\">\\n<title>c</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-18\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-14.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">c</text>\\n</g>\\n<!-- b&#45;&gt;c -->\\n<g id=\\"edge2\\" class=\\"edge\\">\\n<title>b&#45;&gt;c</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\\" />\\n</g>\\n<!-- c&#45;&gt;a -->\\n<g id=\\"edge3\\" class=\\"edge\\">\\n<title>c&#45;&gt;a</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\\" />\\n</g>\\n</g>\\n</svg>\\n</div>\\n\\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\\n\\n<h4 id=\\"q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</h4>\\n\\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 呢?方法如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}{%{% endraw %} raw %}\\n{% raw %}{%{% endraw %} endraw %}\\n</code></pre></div></div>\\n\\n<p>如上,就是用 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 把 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 包起来,但是 <code class=\\"language-plaintext highlighter-rouge\\">%}</code> 不用包。应该讲的很清楚了吧。</p>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<ol>\\n <li><a href=\\"https://bundler.io\\">https://bundler.io</a></li>\\n <li><a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></li>\\n <li><a href=\\"https://zhuanlan.zhihu.com/p/87225594\\">https://zhuanlan.zhihu.com/p/87225594</a></li>\\n <li><a href=\\"https://chat.openai.com/chat\\">https://chat.openai.com/chat</a></li>\\n <li><a href=\\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\\n <li><a href=\\"https://github.com/dyutibarma/monochrome\\">https://github.com/dyutibarma/monochrome</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\\n <li><a href=\\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\\n <li><a href=\\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的 Jekyll 快速教程</title>\\n \\t<meta name=\\"description\\" content=\\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的 Jekyll 快速教程</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-23T19:43:02+00:00\\" class=\\"by-line\\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\\n\\n<h3 id=\\"part-1基本特点\\">Part 1、基本特点</h3>\\n\\n<h4 id=\\"一基本语法\\">一、基本语法</h4>\\n\\n<ul>\\n <li>变量:用双大括号表示变量 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code></li>\\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\\n <li>支持循环与分支结构:比如 <code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">if-elsif-else-endif</code> :可以使用 <code class=\\"language-plaintext highlighter-rouge\\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\\n</ul>\\n\\n<h4 id=\\"二典型-jekyll-项目结构及重要文件介绍\\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\\n\\n<h5 id=\\"1配置文件-_configyml\\">1、配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code></h5>\\n\\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\\"language-plaintext highlighter-rouge\\">encoding: utf-8</code> 这一条。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Site settings</span>\\n<span class=\\"na\\">encoding</span><span class=\\"pi\\">:</span> <span class=\\"s\\">utf-8</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">麦克船长的技术、产品与商业博客</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\\"</span>\\n<span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptain.com\\"</span>\\n<span class=\\"na\\">author</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">name</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Your</span><span class=\\"nv\\"> </span><span class=\\"s\\">Name\\"</span>\\n <span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptian.com\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Markdown and highlighter</span>\\n<span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Plugins</span>\\n<span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-paginate</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-sitemap</span>\\n</code></pre></div></div>\\n\\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Build settings</span>\\n<span class=\\"na\\">baseurl</span><span class=\\"pi\\">:</span> <span class=\\"c1\\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\\n<span class=\\"na\\">source</span><span class=\\"pi\\">:</span> <span class=\\"s\\">.</span>\\n<span class=\\"na\\">destination</span><span class=\\"pi\\">:</span> <span class=\\"s\\">./_site</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:title</span>\\n<span class=\\"na\\">paginate</span><span class=\\"pi\\">:</span> <span class=\\"m\\">20</span>\\n<span class=\\"na\\">paginate_path</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/page:num/</span>\\n<span class=\\"na\\">collections</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">posts</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">output</span><span class=\\"pi\\">:</span> <span class=\\"no\\">true</span>\\n <span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:year/:month/:day/:title/</span>\\n <span class=\\"na\\">directory</span><span class=\\"pi\\">:</span> <span class=\\"s\\">_posts</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2布局文件_layouts-目录下的文件规则\\">2、布局文件:<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的文件规则</h5>\\n\\n<p>Jekyll 的 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\\"language-plaintext highlighter-rouge\\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\\"language-plaintext highlighter-rouge\\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\\n\\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\\n\\n<h5 id=\\"3页面文件及其头部\\">3、页面文件及其头部</h5>\\n\\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">page</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/categories/</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">Categories</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>每个页面文件的头部都会有layout,并与 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的某个文件对应。</p>\\n\\n<h3 id=\\"part-2jekyll-中的全局变量\\">Part 2、Jekyll 中的全局变量</h3>\\n\\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:包含当前页面或文章的内容。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:包含有关网站的所有标签的信息。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\\n</ul>\\n\\n<p>这些变量可以在模板中使用,比如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;h1&gt;</span>{{ page.title }}<span class=\\"nt\\">&lt;/h1&gt;</span>\\n<span class=\\"nt\\">&lt;p&gt;</span>{{ site.description }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n<span class=\\"nt\\">&lt;ul&gt;</span>\\n {{ for category in site.categories %}\\n <span class=\\"nt\\">&lt;li&gt;</span>{{ category }}<span class=\\"nt\\">&lt;/li&gt;</span>\\n {{ endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span> \\n</code></pre></div></div>\\n\\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">my_custom_variable</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Hello</span><span class=\\"nv\\"> </span><span class=\\"s\\">World\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后就可以在模板文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\\n\\n<h4 id=\\"一site变量\\">一、site变量</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\\n\\n<h5 id=\\"1sitecategories\\">1、<code class=\\"language-plaintext highlighter-rouge\\">site.categories</code></h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\\"language-plaintext highlighter-rouge\\">first</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">category</code> 的名字,如下使用:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2sitepages\\">2、site.pages</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3其他常用属性\\">3、其他常用属性</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.title</code>:是网站的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.related_posts</code>:相关文章的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.data</code>:从 <code class=\\"language-plaintext highlighter-rouge\\">_data</code> 目录加载的数据。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.static_files</code>:静态文件的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.collections</code>:自定义集合的列表。</li>\\n</ul>\\n\\n<h4 id=\\"二page-变量\\">二、<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量</h4>\\n\\n<p>在 Jekyll 中,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量属性包括:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">layout</code>:表示页面使用的布局模板的名称。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">title</code>:表示页面的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">date</code>:表示页面的发布日期。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">categories</code>:表示页面所属的分类列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:表示页面所属的标签列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\\n</ul>\\n\\n<p>在模板中,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.属性名 }}</code> 的方式来访问 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.title }}</code>。此外,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量还有其他属性,如 <code class=\\"language-plaintext highlighter-rouge\\">permalink</code>、<code class=\\"language-plaintext highlighter-rouge\\">excerpt</code>、<code class=\\"language-plaintext highlighter-rouge\\">url</code> 等,可以根据需要调用。</p>\\n\\n<h3 id=\\"part-3控制结构\\">Part 3、控制结构</h3>\\n\\n<h4 id=\\"1if-else-分支结构\\">1、<code class=\\"language-plaintext highlighter-rouge\\">if-else</code> 分支结构</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ if tmp_var == \\"type1\\" %}\\n{{ elsif tmp_var == \\"type2\\" %}\\n{{ elsif tmp_var == \\"type3\\" %}\\n{{ elsif tmp_var == \\"type4\\" %}\\n{{ else tmp_var == \\"type5\\" %}\\n{{ endif %}\\n</code></pre></div></div>\\n\\n<h4 id=\\"2for-endfor-循环结构\\">2、<code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 循环结构</h4>\\n\\n<p>不带条件判断的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 循环如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for post in paginator.posts %}\\n\\t<span class=\\"c\\">&lt;!-- Your other sentences --&gt;</span>\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<p>带条件循环的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages | where: \\"dir\\", \\"categories\\" %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3jekyll-支持的其他结构包括\\">3、Jekyll 支持的其他结构包括:</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">capture</code> 用于捕获输出的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">cycle</code> 用于循环一组字符串的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">include</code> 用于包含其他文件的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">while</code> 用于在满足指定条件时执行代码的结构。</li>\\n</ul>\\n\\n<h3 id=\\"参考\\">参考:</h3>\\n\\n<p>1、<a href=\\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\\n2、<a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>不要船开远了,就忘了为什么启航</title>\\n \\t<meta name=\\"description\\" content=\\"2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>不要船开远了,就忘了为什么启航</h2>\\t\\t\\n\\t<time datetime=\\"2022-08-11T15:53:57+00:00\\" class=\\"by-line\\">11 Aug 2022, 杭州 | 作者 麦克船长 | 总计 3223 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>偶然翻到 2020.06.11 刚来到阿里时写的一篇内容(我是 2020 年的 6 月 4 日我入职阿里巴巴集团),是有关于来阿里的期待、对这家公司的一些粗浅初步的理解。此时再翻来看看,最大的感触就是,提醒自己勿忘初心。</p>\\n\\n<p>在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:</p>\\n\\n<ul>\\n <li>很多人是带着梦想来阿里的,那么我的梦想是什么呢?</li>\\n <li>最喜欢新六脉的哪句话?为什么?</li>\\n <li>关于阿里企业价值观:为什么要接受这套价值观?</li>\\n <li>价值观的本质意义(极度务实视角)是什么?</li>\\n <li>Landing 的 SOP</li>\\n <li>问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2020 年平安夜 · 百年湖畔 87 期合影</p>\\n\\n<h3 id=\\"很多人是带着梦想来阿里的那么我的梦想是什么呢\\">很多人是带着梦想来阿里的,那么我的梦想是什么呢?</h3>\\n\\n<p>Christensen 在《创新者的窘境》中提到:每一次技术更迭,都需要破坏性创新,而破坏性创新在前一次技术更迭的胜出者内部是很难生长出来的。阿里诞生以来,不断地创造第二增长曲线:阿里巴巴、淘宝、支付宝、天猫、阿里云、钉钉 …… 这让我非常好奇。其中很多产品穿越多个时间周期,期间不断创造内生二次曲线。</p>\\n\\n<p>但是阿里也一样错失了很多,微信、美团、拼多多、抖/快…… 等等很多产品诞生在了其他公司,还有某些产品在不断的科技更迭中自身生长出了第二曲线。</p>\\n\\n<p>因此我来阿里的梦想也非常明确:<strong>参与或创造一次(甚至多次)第二曲线,可以是新产品,也可以是原有产品内生的。在这个过程中获得个人成长、个人价值。</strong></p>\\n\\n<p>一直以来,我有三个最想实现或得到的东西:LOVE、CREATION、FREEDOM。随着生活与工作的前行,对这三者的理解,在不断加深。在这个问题里,我想应该是讨论”CREATION”。</p>\\n\\n<p>CREATION 上,我的梦想的范式,大概是从自己中学时代就确立了,在某一次人类社会变革浪潮中,扮演有一定权重的角色。这里面有几个变量:<strong>什么领域(F)的变革;什么规模(S)的变革;多大的权重(W);什么角色(R)。</strong></p>\\n\\n<p>F 这个变量,我在中学及大学时代逐渐明确,是以相对普适的产品形式输出结果并对社会变革产生积极作用。后来越来越明确为科技与商业结合的领域。</p>\\n\\n<p>S、W 这两个变量,自然是越大越好。因此我会希望能够构建尽可能大的机会,或者参与到尽可能大的机会中。R 希望是有强烈 Ownership 的身份。</p>\\n\\n<p>因此过去几年我选择了创业。创业就像冲浪,你抓住一次浪并完成漂亮的动作,就是一次不算失败的创业。但是如果一个浪没抓住,你去追它是没意义的,而应该等待下一个浪。我认为在未来 5~10 年内难以出现规模能大到令我足够兴奋的科技浪潮。大浪潮中属于创业者的大机会很多,而中小浪潮的大机会基本只属于大平台,那么为了在壮年期做获得我的 CREATION,我选择了加入阿里这样的大平台。</p>\\n\\n<p>在最后做决定以及初来阿里的那个人生转折点,作为老阿里人的曲洋老师对我说的一句话,深深地鼓励了我,他说:”带着创业气质,把这里当你的舞台折腾!”</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2020 年双十一 · 淘宝 KO</p>\\n\\n<h3 id=\\"最喜欢新六脉的哪句话为什么\\">最喜欢新六脉的哪句话?为什么?</h3>\\n\\n<p>最喜欢的是“因为信任所以简单”。</p>\\n\\n<p>我一直认为人最重要的两个元特质是”真实”和“谦逊”,由”真实”可以塑造自我(对内)、构建信任(对外),后者可以带来清晰的边界,继而实现人与人之间高效的互动(这种互动包括各种人际关系在内,如婚姻、合伙、共事、合作等等)。</p>\\n\\n<p><strong>事物(虚实皆可)呈现在人的认知中,会得到三方面的投影:facts、opinion、feeling。如果我们足够真实,当我们需要把这三方面呈现给他人时,双方就能顺畅建立信任。信任的结果,就对应到这三方面:彼此之间建立共识(facts)、求同存异(opinion)、尊重感受(feeling),这就是”简单”。</strong></p>\\n\\n<p>另外一句是鼓励自己勇于绽放的一条:「此次此刻,非我莫属」。</p>\\n\\n<p>激情、自信、积极…… 通常行为统一表现为“勇于绽放自己”,绽放有表达(语言)与投身(行为)两种表现形式。更进一步推进就是”此次此刻,非我莫属”的阿里价值观。</p>\\n\\n<p>低调、稳重、谦逊,其实与“此次此刻,非我莫属“,并不矛盾。这点是我来到阿里后,发现自己在过去这些年的创业中已经不知不觉改变了,从 Introvert 逐渐变成了 Extrovert 的人,而且从曾经 social 中消耗能量,逐渐变为我现在可以感知到获得能量。这种变化,是我最近来阿里才确认发生的,此前因为自己创业者的身份没有察觉这种变化的发生。</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年秋 · 径山之行</p>\\n\\n<h3 id=\\"关于阿里企业价值观为什么要接受这套价值观\\">关于阿里企业价值观:为什么要接受这套价值观?</h3>\\n\\n<p>马老师和老逍都提到这个:我们是寻找同路人,而不是教育别人。这其实非常明晰地解释了为什么阿里要构建一个毛细血管网络一样的政委体系。基于这种用人理念,政委体系不敢说是最优解,但一定是优解(而且是否有更优解的论证没有意义)。</p>\\n\\n<p>对于个人,我的理解是要做两件事:<strong>1)构建自己的价值观体系(初始化);2)寻找价值观契合的公司(做匹配)。这两点里,没有任何地方提到”你要改变价值观为了契合你所在的公司”。</strong></p>\\n\\n<p>而企业价值观呢,其实可以分两部分看待:普世价值观、独特价值观。前者因为是普世的,所以到了哪个公司这种价值观都对,这点老逍也提了,比如“客户第一”。后者是个性化的,但不存在孰高孰低,就像一个人内向还是外向,你不能说哪个是错的。</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-4.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年双十一 · 天天特卖团队</p>\\n\\n<h3 id=\\"价值观的本质意义极度务实视角是什么\\">价值观的本质意义(极度务实视角)是什么?</h3>\\n\\n<p><strong>在充分考虑价值观适配使命、愿景基础上,价值观本身的意义,在和风细雨时(即企业价值观与其他价值判断相 match 时),是看不到的。但在暴风骤雨时(即企业价值观与其他价值判断相冲突时),就能显示其实实在在的作用了。</strong>我认为包括三类,前两个是阿里整体视角,第三个是阿里内部:</p>\\n\\n<ul>\\n <li>经济体内,阿里与其他生态位的冲突或损益关系,如曾经的美蘑口一役。</li>\\n <li>经济体内,其他的生态位之间的冲突或损益关系,如曾经的十月围城。</li>\\n <li>阿里人的行为价值判断,如最近的钉钉代考事件、过往的各类廉政事件。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-5.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年冬 · 淘宝天猫合并前合影</p>\\n\\n<h3 id=\\"landing的sop\\">Landing 的 SOP</h3>\\n\\n<p>大家都说 landing 充满挑战,马老师其实给出了 landing 的 SOP 三部曲:<strong>一起打过仗、一起创过新、一起度过难。三个经历都 close 才算 smooth landing。</strong></p>\\n\\n<p>集团人才策略层面、HR 实操层面、Leader 层面、,对于新人 landing 能做到什么程度的保障,其实每个新人感受到的不尽相同:</p>\\n\\n<ul>\\n <li>集团层面,始终是在构建更好的新人 landing 环境的,这符合自身价值,这能打下很好的底子。</li>\\n <li>实操层面,包括面试阶段对候选人的价值观判断、预期管理,面试及入职后公司文化及人才体系的事实呈现、内化吸收和长期解惑。</li>\\n <li>Leader 层面,这是新人体感最强烈的部分,也是最重要的部分。尽管拥抱变化,但首先 Leader 需要给出尽可能最全面的考虑,其次是对候选人的预期管理。好的 Leader 会给候选人提供合理的着陆点、多个降落伞、缓冲垫,完成 smooth landing。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-6.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年夏 · 出差厦漳泉</p>\\n\\n<h3 id=\\"问问自己来到阿里如果初期我可能需要做一点改变那会是什么\\">问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</h3>\\n\\n<p>曾经个人的激情与动力,常来自于“增长”。常说<strong>高增长掩盖一切</strong>,所以未来在阿里如果不能如创业般快速获得反馈得到积极结果,并且大平台中必然要接受大量关联方共同参与项目而导致的效率降低,因此我要逐渐改变自己,重新适应这种环境下的激情与动力获得方式。</p>\\n\\n<p>另一方面,作为创业公司的负责人,工作中鲜有因为内部原因而无法推进的事情,但是扮演肩部或腰部角色时,需要接受头部决策的一定程度不可控,这是我需要作出的适应与改变。关于这一点,我在几个月前就已经在做预期管理和心态调整,我认为以创业者的强适应性,这可能并不会是问题,但是我习惯于保持谨慎的乐观来面对自己。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\\n \\t<meta name=\\"description\\" content=\\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-11T15:59:57+00:00\\" class=\\"by-line\\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2022-12-11-wechat-chatgpt-3.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\\n\\n<h3 id=\\"stepbystep\\">Step by step</h3>\\n\\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\\n\\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\\n\\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\\n\\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\\n\\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\\n\\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\\n\\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">chatGPTAccountPool</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"na\\">email</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your email&gt;</span>\\n <span class=\\"na\\">password</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your password&gt;</span>\\n<span class=\\"c1\\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\\n<span class=\\"na\\">chatPrivateTiggerKeyword</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker pull holegots/wechat-chatgpt:latest。\\n</code></pre></div></div>\\n\\n<p>第八步,启动 wechat-chatgpt:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker run <span class=\\"nt\\">-d</span> <span class=\\"nt\\">--name</span> wechat-chatgpt <span class=\\"nt\\">-v</span> <span class=\\"si\\">$(</span><span class=\\"nb\\">pwd</span><span class=\\"si\\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\\n</code></pre></div></div>\\n\\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\\"language-plaintext highlighter-rouge\\">npm install &amp;&amp; poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-1.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-2.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\\n\\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker logs <span class=\\"nt\\">-f</span> wechat-chatgpt\\n</code></pre></div></div>\\n\\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\\n\\n<p>如果要停止运行,用如下命令:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker stop wechat-chatgpt\\n</code></pre></div></div>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<p>1、<a href=\\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\\n \\t<meta name=\\"description\\" content=\\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-17T15:08:01+00:00\\" class=\\"by-line\\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一关于-bert-的一些背景\\" id=\\"markdown-toc-一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</a></li>\\n <li><a href=\\"#二开始一个-bert-的动手小试验\\" id=\\"markdown-toc-二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</a> <ul>\\n <li><a href=\\"#1安装-anaconda-来为部署-bert-做环境准备\\" id=\\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\\n <li><a href=\\"#2安装-bert-所需要的各种依赖\\" id=\\"markdown-toc-2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</a></li>\\n <li><a href=\\"#3下载一个预训练pre-train过的-bert-模型\\" id=\\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\\n <li><a href=\\"#5启动-bert-服务端\\" id=\\"markdown-toc-5启动-bert-服务端\\">5、启动 BERT 服务端</a></li>\\n <li><a href=\\"#6在-pycharm-中使用-conda-的环境\\" id=\\"markdown-toc-6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</a></li>\\n <li><a href=\\"#7编写程序实现-bert-客户端\\" id=\\"markdown-toc-7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三bert-模型的优劣势及其原因\\" id=\\"markdown-toc-三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</a> <ul>\\n <li><a href=\\"#1bert-的优势是很明显的\\" id=\\"markdown-toc-1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</a> <ul>\\n <li><a href=\\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\" id=\\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\\n <li><a href=\\"#12识别并专注于较重要的部分进行文本处理\\" id=\\"markdown-toc-12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\\n <li><a href=\\"#13快速构建针对具体任务的-nlp-系统\\" id=\\"markdown-toc-13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2bert-模型的劣势及其原因\\" id=\\"markdown-toc-2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</a> <ul>\\n <li><a href=\\"#21随机挖-mask-的完形填空题是有隐患的\\" id=\\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\\n <li><a href=\\"#22nsp-任务有必要吗\\" id=\\"markdown-toc-22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</a></li>\\n <li><a href=\\"#23针对两个或以上词组成的连续词的词义被丢失\\" id=\\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\\n <li><a href=\\"#24需要的算力高\\" id=\\"markdown-toc-24需要的算力高\\">2.4、需要的算力高</a></li>\\n <li><a href=\\"#25需要的模型大\\" id=\\"markdown-toc-25需要的模型大\\">2.5、需要的模型大</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四一些关于-bert-的问题\\" id=\\"markdown-toc-四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</a> <ul>\\n <li><a href=\\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\\" id=\\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\\n <li><a href=\\"#2为什么-bert-可以比-rnn-更好地并行化\\" id=\\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</h3>\\n\\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\\n\\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\\n\\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\\n\\n<h3 id=\\"二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</h3>\\n\\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\\n\\n<h4 id=\\"1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\\n\\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\\n\\n<p>更新 conda 到最新版本:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda update <span class=\\"nt\\">-n</span> base conda\\n</code></pre></div></div>\\n\\n<p>使用 Python 3.7 创建一个新的环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda create <span class=\\"nt\\">-n</span> py37 <span class=\\"nv\\">python</span><span class=\\"o\\">=</span>3.7\\n</code></pre></div></div>\\n\\n<p>激活这个新环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda activate py37\\n</code></pre></div></div>\\n\\n<p>验证正在使用的是正确版本的 Python</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>python <span class=\\"nt\\">--version</span>\\n</code></pre></div></div>\\n\\n<p>另外你可能还会用到的 conda 命令有:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\\nconda deactivate py37\\n\\n<span class=\\"c\\"># 查看 conda 当前安装的所有库</span>\\nconda list\\n</code></pre></div></div>\\n\\n<h4 id=\\"2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda <span class=\\"nb\\">install </span><span class=\\"nv\\">tensorflow</span><span class=\\"o\\">==</span>1.14.0\\n</code></pre></div></div>\\n\\n<p>验证 tensorflow 是否安装正确:</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">import</span> <span class=\\"nn\\">tensorflow</span> <span class=\\"k\\">as</span> <span class=\\"n\\">tf</span>\\n<span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"n\\">tf</span><span class=\\"p\\">.</span><span class=\\"n\\">__version__</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\\n\\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\\n\\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\\n\\n<ul>\\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n</ul>\\n\\n<p>4、安装 BERT 的服务端和客户端</p>\\n\\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\\n\\n<p>在你激活的环境里,安装 <code class=\\"language-plaintext highlighter-rouge\\">bert-as-service</code>:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 安装服务端和客户端</span>\\n<span class=\\"c\\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\\nconda <span class=\\"nb\\">install </span>bert-serving-server bert-serving-client \\n验证 bert-as-service 是否安装成功\\nbert-serving-start <span class=\\"nt\\">-h</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5启动-bert-服务端\\">5、启动 BERT 服务端</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 命令行下启动BERT服务</span>\\n<span class=\\"c\\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\\nbert-serving-start <span class=\\"nt\\">-model_dir</span> /模型/的/绝对/路径 <span class=\\"nt\\">-num_worker</span><span class=\\"o\\">=</span>4\\n</code></pre></div></div>\\n\\n<h4 id=\\"6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</h4>\\n\\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\\n\\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\\"language-plaintext highlighter-rouge\\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\\n\\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\\n\\n<h4 id=\\"7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</h4>\\n\\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">from</span> <span class=\\"nn\\">bert_serving.client</span> <span class=\\"kn\\">import</span> <span class=\\"n\\">BertClient</span>\\n<span class=\\"kn\\">import</span> <span class=\\"nn\\">numpy</span> <span class=\\"k\\">as</span> <span class=\\"n\\">np</span>\\n\\n<span class=\\"c1\\"># 定义类\\n</span><span class=\\"k\\">class</span> <span class=\\"nc\\">BertModel</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">__init__</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"k\\">try</span><span class=\\"p\\">:</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertClient</span><span class=\\"p\\">(</span><span class=\\"n\\">ip</span><span class=\\"o\\">=</span><span class=\\"s\\">'127.0.0.1'</span><span class=\\"p\\">,</span> <span class=\\"n\\">port</span><span class=\\"o\\">=</span><span class=\\"mi\\">5555</span><span class=\\"p\\">,</span> <span class=\\"n\\">port_out</span><span class=\\"o\\">=</span><span class=\\"mi\\">5556</span><span class=\\"p\\">)</span> <span class=\\"c1\\"># 创建客户端对象\\n</span> <span class=\\"c1\\"># 注意:可以参考API,查看其它参数的设置\\n</span> <span class=\\"c1\\"># 127.0.0.1 表示本机IP,也可以用localhost\\n</span> <span class=\\"k\\">except</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">raise</span> <span class=\\"nb\\">Exception</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cannot create BertClient\\"</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">close_bert</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">text</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''对输入文本进行embedding\\n Args:\\n text: str, 输入文本\\n Returns:\\n text_vector: float, 返回一个列表,包含text的embedding编码值\\n '''</span>\\n <span class=\\"n\\">text_vector</span> <span class=\\"o\\">=</span> <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">encode</span><span class=\\"p\\">([</span><span class=\\"n\\">text</span><span class=\\"p\\">])[</span><span class=\\"mi\\">0</span><span class=\\"p\\">]</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">text_vector</span> <span class=\\"c1\\"># 获取输出结果\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_1</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_2</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''根据两个语句的vector,计算它们的相似性\\n Args:\\n vec_1: float, 语句1的vector\\n vec_2: float, 语句2的vector\\n Returns:\\n sim_value: float, 返回相似性的计算值\\n '''</span>\\n <span class=\\"c1\\"># 根据cosine的计算公式\\n</span> <span class=\\"n\\">v1</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_1</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">v2</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">float</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span> <span class=\\"o\\">*</span> <span class=\\"n\\">v2</span><span class=\\"p\\">.</span><span class=\\"n\\">T</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span><span class=\\"p\\">)</span> <span class=\\"o\\">*</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">cosine</span> <span class=\\"o\\">=</span> <span class=\\"n\\">a</span> <span class=\\"o\\">/</span> <span class=\\"n\\">b</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">cosine</span>\\n\\n\\n<span class=\\"k\\">if</span> <span class=\\"n\\">__name__</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"__main__\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># 创建bert对象\\n</span> <span class=\\"n\\">bert</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertModel</span><span class=\\"p\\">()</span>\\n <span class=\\"k\\">while</span> <span class=\\"bp\\">True</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># --- 输入语句 ----\\n</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句1: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">if</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"N\\"</span> <span class=\\"ow\\">or</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"n\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">close_bert</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span> <span class=\\"k\\">break</span>\\n\\n <span class=\\"n\\">input_b</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句2: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># --- 对输入语句进行embedding ---\\n</span>\\n <span class=\\"n\\">a_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_a</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'a_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">a_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"n\\">b_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_b</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'b_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 计算两个语句的相似性\\n</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"n\\">a_vec</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'cosine value : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">cos</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'</span><span class=\\"se\\">\\\\n\\\\n</span><span class=\\"s\\">'</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\\n</span> <span class=\\"k\\">if</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">&gt;</span> <span class=\\"mf\\">0.85</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"2个语句的含义相似\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">else</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"不相似\\"</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<p>在使用 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 连接 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 时,你需要确保 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 使用的模型和 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\\n\\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>请输入语句1: \\n请输入语句2: \\n</code></pre></div></div>\\n\\n<p>两句输入好确认后,得到如下形式的结果:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>a_vec shape : (768,)\\nb_vec shape : (768,)\\ncosine value : 0.8691698561422959\\n</code></pre></div></div>\\n\\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\\n\\n<h3 id=\\"三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</h3>\\n\\n<p>论文地址:<a href=\\"https://arxiv.org/abs/1810.04805\\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\\n\\n<h4 id=\\"1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</h4>\\n\\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\\n\\n<blockquote>\\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\\n</blockquote>\\n\\n<h5 id=\\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\\n\\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\\n\\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\\n\\n<h5 id=\\"12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</h5>\\n\\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\\n\\n<h5 id=\\"13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</h5>\\n\\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\\n\\n<h4 id=\\"2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</h4>\\n\\n<h5 id=\\"21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\\n\\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\\n\\n<h5 id=\\"22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</h5>\\n\\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\\n\\n<h5 id=\\"23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\\n\\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\\n\\n<h5 id=\\"24需要的算力高\\">2.4、需要的算力高</h5>\\n\\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\\n\\n<h5 id=\\"25需要的模型大\\">2.5、需要的模型大</h5>\\n\\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\\n\\n<h3 id=\\"四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</h3>\\n\\n<h4 id=\\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\\n\\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\\n\\n<h4 id=\\"2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\\n\\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://arxiv.org/abs/1810.04805</li>\\n <li>https://github.com/google-research/bert</li>\\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\\n \\t<meta name=\\"description\\" content=\\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-24T15:08:01+00:00\\" class=\\"by-line\\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一自然语言处理领域近年的发展关键节点\\" id=\\"markdown-toc-一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</a> <ul>\\n <li><a href=\\"#1从理性主义到经验主义\\" id=\\"markdown-toc-1从理性主义到经验主义\\">1、从理性主义到经验主义</a></li>\\n <li><a href=\\"#2经验主义的早期还不是深度学习\\" id=\\"markdown-toc-2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</a></li>\\n <li><a href=\\"#3撇开特征让机器囫囵吞枣地学吧\\" id=\\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\\n <li><a href=\\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\" id=\\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\\n <li><a href=\\"#5自监督学习法让我们省去人工标注\\" id=\\"markdown-toc-5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</a></li>\\n <li><a href=\\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\\" id=\\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\\n <li><a href=\\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\" id=\\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\\n <li><a href=\\"#8数据和算力有了还不够\\" id=\\"markdown-toc-8数据和算力有了还不够\\">8、数据和算力有了,还不够</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二学术里程碑几篇重量级论文\\" id=\\"markdown-toc-二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</a> <ul>\\n <li><a href=\\"#1提出-transformer-的attention-is-all-you-need2017\\" id=\\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\\n <li><a href=\\"#2elmo-deep-contextualized-word-representations\\" id=\\"markdown-toc-2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</a></li>\\n <li><a href=\\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\" id=\\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\\n <li><a href=\\"#4gpt-3-language-models-are-few-shot-learners2020\\" id=\\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\\n <li><a href=\\"#其他的重量级论文\\" id=\\"markdown-toc-其他的重量级论文\\">其他的重量级论文</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三行业里程碑\\" id=\\"markdown-toc-三行业里程碑\\">三、行业里程碑</a></li>\\n <li><a href=\\"#四成本\\" id=\\"markdown-toc-四成本\\">四、成本</a></li>\\n <li><a href=\\"#五业内应用\\" id=\\"markdown-toc-五业内应用\\">五、业内应用</a></li>\\n <li><a href=\\"#五行业内哪些人的言论值得我们日常重点关注\\" id=\\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</h3>\\n\\n<p><img src=\\"/img/src/2022-12-17-ai-bert-1-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h4 id=\\"1从理性主义到经验主义\\">1、从理性主义到经验主义</h4>\\n\\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\\n\\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\\n\\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\\n\\n<h4 id=\\"2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</h4>\\n\\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\\n\\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\\n\\n<h4 id=\\"3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\\n\\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\\n\\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\\n\\n<h4 id=\\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\\n\\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\\n\\n<h4 id=\\"5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</h4>\\n\\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\\n\\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\\n\\n<h4 id=\\"6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\\n\\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\\n\\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\\n\\n<h4 id=\\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\\n\\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\\n\\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\\n\\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\\n\\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\\n\\n<h4 id=\\"8数据和算力有了还不够\\">8、数据和算力有了,还不够</h4>\\n\\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\\n\\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\\n\\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\\n\\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\\n\\n<h3 id=\\"二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</h3>\\n\\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\\n\\n<h4 id=\\"1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\\n\\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\\n\\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\\n\\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\\n\\n<h4 id=\\"2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</h4>\\n\\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\\n\\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\\n\\n<h4 id=\\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\\n\\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\\n\\n<ul>\\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\\n <li>在预训练中引入自监督学习任务。</li>\\n</ul>\\n\\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\\n\\n<h4 id=\\"4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\\n\\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\\n\\n<h4 id=\\"其他的重量级论文\\">其他的重量级论文</h4>\\n\\n<ul>\\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\\n <li>……</li>\\n</ul>\\n\\n<h3 id=\\"三行业里程碑\\">三、行业里程碑</h3>\\n\\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\\n\\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\\n\\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\\n\\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\\n\\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\\n\\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\\n\\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\\n\\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\\n\\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\\n\\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\\n\\n<h3 id=\\"四成本\\">四、成本</h3>\\n\\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\\n\\n<ul>\\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\\n</ul>\\n\\n<h3 id=\\"五业内应用\\">五、业内应用</h3>\\n\\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\\n\\n<ul>\\n <li>虚拟客服(可以乱真的)</li>\\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\\n <li>智能翻译</li>\\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\\n <li>AI 广告公司:替代传统广告公司</li>\\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\\n</ul>\\n\\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\\n\\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\\n\\n<ul>\\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-7.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-2.jpg\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-8.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年年底,西湖心辰公司发布「<a href=\\"https://www.heyfriday.cn/\\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-1.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</h3>\\n\\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\\n\\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\\n\\n<blockquote>\\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\\n</blockquote>\\n\\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\\n\\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\\n\\n<blockquote>\\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\\n</blockquote>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://beta.openai.com/docs/models</li>\\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\\n <li>https://hub.baai.ac.cn/view/21726</li>\\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\\n <li>https://www.sohu.com/a/615541698_121255906</li>\\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"related_posts":null,"pages":["<h1 id=\\"404---page-not-found\\">404 - Page not found</h1>\\n<p>Sorry, we couldn’t find the requested URL. You can try again by going <a href=\\"/pages/Poechant\\">back to the homepage</a>.</p>\\n","<p><img src=\\"/pages/Poechant/img/about/avatar.jpg\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"麦克船长\\">麦克船长</h3>\\n\\n<p>本名钟超,现任阿里巴巴集团大聚划算运营中心负责人,曾先后负责淘宝行业产品团队、天天特卖等。关注人工智能、电子商务、社交网络、数字人领域。2012 年被评选为 CSDN 中国十大技术博客(CSDN ID:Poechant)。</p>\\n\\n<h4 id=\\"工作经历\\">工作经历</h4>\\n\\n<ul>\\n <li>2020 至今,阿里巴巴集团,<strong>总监/资深综合运营专家(P9)</strong></li>\\n <li>2014 - 2020,七海互联(Qihai Inc.),<strong>CEO &amp; CTO</strong></li>\\n <li>2012 - 2014,领拓云合(LightOnUs),<strong>联合创始人 &amp; 产品总裁</strong></li>\\n <li>2011 - 2012,欢聚集团(纳斯达克上市公司,股票代号 YY),<strong>Web YY 音视频技术负责人</strong></li>\\n</ul>\\n\\n<h4 id=\\"我的学业\\">我的学业</h4>\\n\\n<ul>\\n <li>2011 年,中国科学技术大学,计算机系本科毕业。</li>\\n</ul>\\n\\n<h4 id=\\"我的社交媒体\\">我的社交媒体</h4>\\n\\n<ul>\\n <li>微信:<strong>sinosuperman</strong>(添加请注明「学校/单位/公司」、「职位」和「来意」才能通过,多谢理解!)</li>\\n <li>Bilibili:<a href=\\"https://space.bilibili.com/482553760\\" target=\\"_blank\\"><u>船长模玩</u></a> —— 打小喜欢变形金刚,成年后补偿性消费收藏变形金刚手办😂</li>\\n <li>新浪微博:<a href=\\"http://weibo.com/lauginhom\\" target=\\"_blank\\"><u>@船长还不会游泳</u></a></li>\\n <li>知乎:<a href=\\"https://www.zhihu.com/people/poechant\\" target=\\"_blank\\"><u>船长还不会游泳</u></a></li>\\n</ul>\\n\\n<h4 id=\\"我的爱好\\">我的爱好</h4>\\n\\n<ul>\\n <li>滑雪、拳击</li>\\n <li>电影:豆瓣标记「看过」的影视作品 800 部左右,与数千部的大佬仍有差距(略略略)</li>\\n <li>我的书单:<a href=\\"/booklist/\\">麦克船长的书单</a></li>\\n <li>变形金刚手办收藏</li>\\n</ul>\\n\\n<p><img src=\\"/pages/Poechant/img/about/photo_10.jpg\\" alt=\\"image\\" />\\n<img src=\\"/pages/Poechant/img/about/photo_2.jpg\\" alt=\\"image\\" />\\n<img src=\\"/pages/Poechant/img/about/photo_6.jpg\\" alt=\\"image\\" />\\n<img src=\\"/pages/Poechant/img/about/photo_3.jpg\\" alt=\\"image\\" />\\n<img src=\\"/pages/Poechant/img/about/photo_4.jpg\\" alt=\\"image\\" />\\n<img src=\\"/pages/Poechant/img/about/photo_5.jpg\\" alt=\\"image\\" />\\n<img src=\\"/pages/Poechant/img/about/photo_1.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/about/photo_7.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/about/photo_8.jpg\\" alt=\\"image\\" />\\n<img src=\\"/pages/Poechant/img/about/photo_9.jpg\\" alt=\\"image\\" /></p>\\n","","<p>麦克船长是谁?<a href=\\"/about/\\">这里有个简单的介绍方便了解我</a>。</p>\\n\\n<h3 id=\\"麦克船长的-2022-2023-书单\\">麦克船长的 2022-2023 书单</h3>\\n\\n<p>重点关注:1)人工智能的技术、商业书籍;2)组织行为学;3)电商、零售与供应链管理;4)互联网产品、商业模式;5)Crypto 及 Web3;6)经济学、策略及博弈。</p>\\n\\n<ul>\\n <li>《自然语言处理:基于预训练模型的方法》车万翔</li>\\n <li>《自然语言处理实战:预训练模型应用及其产品化》安库·A·帕特尔</li>\\n <li>《PyTorch 深度学习实战(第 2 版)》弗朗索瓦·肖莱</li>\\n <li>《Python 机器学习经典实例》普拉提克·乔西</li>\\n <li>《深度学习》伊恩·古德费洛</li>\\n <li>《AI 未来进行式》李开复</li>\\n <li>《AI 3.0》梅拉妮·米歇尔</li>\\n <li>《从 AIoT 到元宇宙:关键技术、产业图景与未来展望》</li>\\n <li>《科学之路》杨立昆</li>\\n <li>《虚拟人》玛蒂娜·罗斯布拉特</li>\\n <li>《给大忙人的高效阅读课》</li>\\n <li>《西线无战事》</li>\\n <li>《纳瓦尔宝典》</li>\\n <li>《刀锋》</li>\\n <li>《策略思维:商界、政界及日常生活中的策略竞争》</li>\\n <li>《CEO 管理与组织行为学》尤建新</li>\\n <li>《组织行为学》孙晓岭,中国人民大学出版社</li>\\n <li>《纳瓦尔宝典》</li>\\n <li>《美国四百年:冒险、创新与财富塑造的历史》</li>\\n <li>《历史的温度 1-5 》</li>\\n <li>《自我边界》</li>\\n <li>《以幽默的方式过一生》琢磨先生</li>\\n <li>《半小时漫画经济学金融危机合集》</li>\\n <li>《权力:为什么只有某些人所拥有》杰弗瑞·菲佛</li>\\n <li>《制造消费者:消费主义全球史》</li>\\n <li>《区块链启示录》</li>\\n</ul>\\n","<div id=\\"archives\\">\\n\\n <div class=\\"archive-group\\">\\n \\n <div id=\\"#rt_tech\\"></div>\\n <p></p>\\n\\n <h3 class=\\"category-head\\">rt_tech</h3>\\n <a name=\\"rt_tech\\"></a>\\n <ul>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/08/04/openrtmfp-cumulus-9/\\">OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/07/23/openrtmfp-cumulus-8/\\">OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/06/25/openrtmfp-cumulus-7/\\">OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/06/07/openrtmfp-cumulus-6/\\">OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/04/24/openrtmfp-cumulus-5/\\">OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/04/24/openrtmfp-cumulus-4/\\">OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/04/15/openrtmfp-cumulus-3/\\">OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/04/14/openrtmfp-cumulus-2/\\">OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/04/09/openrtmfp-cumulus-1/\\">OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</a></h4>\\n </article>\\n </li>\\n \\n </ul>\\n </div>\\n\\n <div class=\\"archive-group\\">\\n \\n <div id=\\"#thinking\\"></div>\\n <p></p>\\n\\n <h3 class=\\"category-head\\">thinking</h3>\\n <a name=\\"thinking\\"></a>\\n <ul>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2022/08/11/captain-alibaba/\\">不要船开远了,就忘了为什么启航</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2021/11/11/captain-tttm/\\">欢迎成为「淘宝-天天特卖」团队的创业合伙人!</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2021/06/04/captain-alibaba-1st-anniversary/\\">麦克船长的阿里一年香(入职阿里一周年)</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2020/11/11/captain-double-eleven/\\">担任淘宝产品总负责人的双十一,是怎样的体验?</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2020/04/14/covid2019-catering-business-mode/\\">疫后怎么做餐饮品牌?三叉戟模式或成标配</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2020/04/11/delayed-gratification/\\">延迟满足,才有自由</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2017/02/23/ai-make-people-life-as-billionaires/\\">未来人工智能就是要:让普通人过上现在富豪们的生活</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2017/01/31/danshari-vs-remember-reverberations/\\">我们是应该「断舍离」还是「念念不忘,必有回响」</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2012/05/15/excellent-software-engineer-basic-skills/\\">一名出色软件工程师的技术基本功:编程与工具</a></h4>\\n </article>\\n </li>\\n \\n </ul>\\n </div>\\n\\n <div class=\\"archive-group\\">\\n \\n <div id=\\"#web\\"></div>\\n <p></p>\\n\\n <h3 class=\\"category-head\\">web</h3>\\n <a name=\\"web\\"></a>\\n <ul>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2021/12/23/captains-jeckyll-learning/\\">麦克船长的 Jekyll 快速教程</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2021/12/21/build-github-pages-with-jekyll/\\">如何使用 Jekyll 基于 Github Pages 搭建个人博客</a></h4>\\n </article>\\n </li>\\n \\n </ul>\\n </div>\\n\\n <div class=\\"archive-group\\">\\n \\n <div id=\\"#ai\\"></div>\\n <p></p>\\n\\n <h3 class=\\"category-head\\">ai</h3>\\n <a name=\\"ai\\"></a>\\n <ul>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2022/12/24/captain-nlp-1/\\">自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2022/12/17/ai-bert-1/\\">你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2022/12/11/wechat-chatgpt/\\">动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</a></h4>\\n </article>\\n </li>\\n \\n <li>\\n <article class=\\"archive-item\\">\\n <h4><a href=\\"/pages/Poechant/2022/11/30/midjourney-first-test/\\">确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</a></h4>\\n </article>\\n </li>\\n \\n </ul>\\n </div>\\n\\n</div>","<!-- <pre width=\\"100%\\">\\nsite: {{ site | jsonify | escape }}\\npage: {{ page | jsonify | escape }}\\nlayout: {{ layout | jsonify | escape }}\\ncontent: {{ content | jsonify | escape }}\\npaginator: {{ paginator | jsonify | escape }}\\n</pre> -->\\n\\n<!-- Posts -->\\n<ul id=\\"posts\\">\\n\\n\\t{% for post in paginator.posts %}\\n\\n\\t <li class=\\"post\\">\\n\\t \\t<h2><a href=\\"{% if site.baseurl == \\"/\\" %}{{ post.url }}{% else %}{{ post.url | prepend: site.baseurl }}{% endif %}\\">{%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}</a></h2>\\n <time datetime=\\"{{ post.date | date_to_xmlschema }}\\" class=\\"by-line\\"> <i>{{ post.date | date_to_string }}</i> </time>\\n\\t \\t<p>{{ post.excerpt | strip_html | truncatewords:50 }}</p>\\n\\t </li>\\n\\n {% endfor %}\\n\\n</ul>\\n","//Import\\n@import \\"base\\", \\"mixin\\", \\"typography\\", \\"layout\\", \\"syntax.scss\\", \\"custom.scss\\";\\n","","","","@import \\"jekyll-theme-primer\\";\\n","<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>{% if page.xsl %}<?xml-stylesheet type=\\"text/xml\\" href=\\"{{ '/feed.xslt.xml' | absolute_url }}\\"?>{% endif %}<feed xmlns=\\"http://www.w3.org/2005/Atom\\" {% if site.lang %}xml:lang=\\"{{ site.lang }}\\"{% endif %}><generator uri=\\"https://jekyllrb.com/\\" version=\\"{{ jekyll.version }}\\">Jekyll</generator><link href=\\"{{ page.url | absolute_url }}\\" rel=\\"self\\" type=\\"application/atom+xml\\" /><link href=\\"{{ '/' | absolute_url }}\\" rel=\\"alternate\\" type=\\"text/html\\" {% if site.lang %}hreflang=\\"{{ site.lang }}\\" {% endif %}/><updated>{{ site.time | date_to_xmlschema }}</updated><id>{{ page.url | absolute_url | xml_escape }}</id>{% assign title = site.title | default: site.name %}{% if page.collection != \\"posts\\" %}{% assign collection = page.collection | capitalize %}{% assign title = title | append: \\" | \\" | append: collection %}{% endif %}{% if page.category %}{% assign category = page.category | capitalize %}{% assign title = title | append: \\" | \\" | append: category %}{% endif %}{% if title %}<title type=\\"html\\">{{ title | smartify | xml_escape }}</title>{% endif %}{% if site.description %}<subtitle>{{ site.description | xml_escape }}</subtitle>{% endif %}{% if site.author %}<author><name>{{ site.author.name | default: site.author | xml_escape }}</name>{% if site.author.email %}<email>{{ site.author.email | xml_escape }}</email>{% endif %}{% if site.author.uri %}<uri>{{ site.author.uri | xml_escape }}</uri>{% endif %}</author>{% endif %}{% if page.tags %}{% assign posts = site.tags[page.tags] %}{% else %}{% assign posts = site[page.collection] %}{% endif %}{% if page.category %}{% assign posts = posts | where: \\"category\\", page.category %}{% endif %}{% unless site.show_drafts %}{% assign posts = posts | where_exp: \\"post\\", \\"post.draft != true\\" %}{% endunless %}{% assign posts = posts | sort: \\"date\\" | reverse %}{% assign posts_limit = site.feed.posts_limit | default: 10 %}{% for post in posts limit: posts_limit %}<entry{% if post.lang %}{{\\" \\"}}xml:lang=\\"{{ post.lang }}\\"{% endif %}>{% assign post_title = post.title | smartify | strip_html | normalize_whitespace | xml_escape %}<title type=\\"html\\">{{ post_title }}</title><link href=\\"{{ post.url | absolute_url }}\\" rel=\\"alternate\\" type=\\"text/html\\" title=\\"{{ post_title }}\\" /><published>{{ post.date | date_to_xmlschema }}</published><updated>{{ post.last_modified_at | default: post.date | date_to_xmlschema }}</updated><id>{{ post.id | absolute_url | xml_escape }}</id>{% assign excerpt_only = post.feed.excerpt_only | default: site.feed.excerpt_only %}{% unless excerpt_only %}<content type=\\"html\\" xml:base=\\"{{ post.url | absolute_url | xml_escape }}\\">{{ post.content | strip | xml_escape }}</content>{% endunless %}{% assign post_author = post.author | default: post.authors[0] | default: site.author %}{% assign post_author = site.data.authors[post_author] | default: post_author %}{% assign post_author_email = post_author.email | default: nil %}{% assign post_author_uri = post_author.uri | default: nil %}{% assign post_author_name = post_author.name | default: post_author %}<author><name>{{ post_author_name | default: \\"\\" | xml_escape }}</name>{% if post_author_email %}<email>{{ post_author_email | xml_escape }}</email>{% endif %}{% if post_author_uri %}<uri>{{ post_author_uri | xml_escape }}</uri>{% endif %}</author>{% if post.category %}<category term=\\"{{ post.category | xml_escape }}\\" />{% elsif post.categories %}{% for category in post.categories %}<category term=\\"{{ category | xml_escape }}\\" />{% endfor %}{% endif %}{% for tag in post.tags %}<category term=\\"{{ tag | xml_escape }}\\" />{% endfor %}{% if post.excerpt and post.excerpt != empty %}<summary type=\\"html\\">{{ post.excerpt | strip_html | normalize_whitespace | xml_escape }}</summary>{% endif %}{% assign post_image = post.image.path | default: post.image %}{% if post_image %}{% unless post_image contains \\"://\\" %}{% assign post_image = post_image | absolute_url %}{% endunless %}<media:thumbnail xmlns:media=\\"http://search.yahoo.com/mrss/\\" url=\\"{{ post_image | xml_escape }}\\" /><media:content medium=\\"image\\" url=\\"{{ post_image | xml_escape }}\\" xmlns:media=\\"http://search.yahoo.com/mrss/\\" />{% endif %}</entry>{% endfor %}</feed>","<!-- <pre width=\\"100%\\">\\nsite: {{ site | jsonify | escape }}\\npage: {{ page | jsonify | escape }}\\nlayout: {{ layout | jsonify | escape }}\\ncontent: {{ content | jsonify | escape }}\\npaginator: {{ paginator | jsonify | escape }}\\n</pre> -->\\n\\n<!-- Posts -->\\n<ul id=\\"posts\\">\\n\\n\\t{% for post in paginator.posts %}\\n\\n\\t <li class=\\"post\\">\\n\\t \\t<h2><a href=\\"{% if site.baseurl == \\"/\\" %}{{ post.url }}{% else %}{{ post.url | prepend: site.baseurl }}{% endif %}\\">{%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}</a></h2>\\n <time datetime=\\"{{ post.date | date_to_xmlschema }}\\" class=\\"by-line\\"> <i>{{ post.date | date_to_string }}</i> </time>\\n\\t \\t<p>{{ post.excerpt | strip_html | truncatewords:50 }}</p>\\n\\t </li>\\n\\n {% endfor %}\\n\\n</ul>\\n","<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>\\n{% if page.xsl %}<?xml-stylesheet type=\\"text/xsl\\" href=\\"{{ \\"/sitemap.xsl\\" | absolute_url }}\\"?>\\n{% endif %}<urlset xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\" xsi:schemaLocation=\\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\\" xmlns=\\"http://www.sitemaps.org/schemas/sitemap/0.9\\">\\n{% assign collections = site.collections | where_exp:'collection','collection.output != false' %}{% for collection in collections %}{% assign docs = collection.docs | where_exp:'doc','doc.sitemap != false' %}{% for doc in docs %}<url>\\n<loc>{{ doc.url | replace:'/index.html','/' | absolute_url | xml_escape }}</loc>\\n{% if doc.last_modified_at or doc.date %}<lastmod>{{ doc.last_modified_at | default: doc.date | date_to_xmlschema }}</lastmod>\\n{% endif %}</url>\\n{% endfor %}{% endfor %}{% assign pages = site.html_pages | where_exp:'doc','doc.sitemap != false' | where_exp:'doc','doc.url != \\"/404.html\\"' %}{% for page in pages %}<url>\\n<loc>{{ page.url | replace:'/index.html','/' | absolute_url | xml_escape }}</loc>\\n{% if page.last_modified_at %}<lastmod>{{ page.last_modified_at | date_to_xmlschema }}</lastmod>\\n{% endif %}</url>\\n{% endfor %}{% assign static_files = page.static_files | where_exp:'page','page.sitemap != false' | where_exp:'page','page.name != \\"404.html\\"' %}{% for file in static_files %}<url>\\n<loc>{{ file.path | replace:'/index.html','/' | absolute_url | xml_escape }}</loc>\\n<lastmod>{{ file.modified_time | date_to_xmlschema }}</lastmod>\\n</url>\\n{% endfor %}</urlset>\\n","Sitemap: {{ \\"sitemap.xml\\" | absolute_url }}\\n"],"time":"2023-01-03 19:14:34 +0000","tags":{"直播技术":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-08-04T17:58:17+00:00\\" class=\\"by-line\\">04 Aug 2012, 广州 | 作者 麦克船长 | 总计 5236 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfpserver-线程的启动和等待\\" id=\\"markdown-toc-一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</a> <ul>\\n <li><a href=\\"#1pocothread\\" id=\\"markdown-toc-1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></a></li>\\n <li><a href=\\"#2封装一个可运行线程的类\\" id=\\"markdown-toc-2封装一个可运行线程的类\\">2、封装一个可运行线程的类</a></li>\\n <li><a href=\\"#3启动-rtmfpserver-线程\\" id=\\"markdown-toc-3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</a></li>\\n <li><a href=\\"#4rtmfpserver-线程等待\\" id=\\"markdown-toc-4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二rtmfpmanager-对-rtmfpserver-的影响\\" id=\\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</a></li>\\n</ul>\\n\\n<h3 id=\\"一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</h3>\\n\\n<h4 id=\\"1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></h4>\\n\\n<p>Cumulus 大量使用了 <code class=\\"language-plaintext highlighter-rouge\\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PoechantRunnable</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// your codes</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">};</span>\\n \\n<span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">PoechantRunnable</span> <span class=\\"n\\">runnable</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Image that it's a gift</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">thread</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// And… thread is just like your girl</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">runnable</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Okay, give your sweet babe the gift :)</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2封装一个可运行线程的类\\">2、封装一个可运行线程的类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中实现了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 类,该类继承了 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,就是上面那个 <code class=\\"language-plaintext highlighter-rouge\\">gift</code> 喽。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">StartableProcess</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span><span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">);</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>可以看到其中有 <code class=\\"language-plaintext highlighter-rouge\\">Startable&amp; _startable</code> 引用成员,它并没有继承 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,而是封装了 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">_thread</span><span class=\\"p\\">;</span>\\n<span class=\\"n\\">StartableProcess</span> <span class=\\"n\\">_process</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>这里 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 封装了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 成员,与 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\\n\\n<h4 id=\\"3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</h4>\\n<p>我们可以看到在 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的构造函数中初始化了 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\\"language-plaintext highlighter-rouge\\">(Flag Field)_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,因为它还没有调用启动函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_name</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_stop</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_process</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>初始化 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 时,调用 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">StartableProcess</span><span class=\\"o\\">::</span><span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">(</span><span class=\\"n\\">startable</span><span class=\\"p\\">){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>传入 <code class=\\"language-plaintext highlighter-rouge\\">_startable</code> 的引用。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的,然后通过调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 来启动,启动后会响应到 <code class=\\"language-plaintext highlighter-rouge\\">run()</code>。下面我们以 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程为例。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 类是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPServer</span>\\n <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Gateway</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">protected</span> <span class=\\"n\\">Handler</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">SocketHandler</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">RTMFPServer</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">cores</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">(</span><span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>所在线程</th>\\n <th>调用者</th>\\n <th>函数</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>主线程</td>\\n <td>main(…)</td>\\n <td> </td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer从Startable继承来的Thread成员</td>\\n <td>Thread::start(…)</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\\n <td>StartableProcess::run()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::run()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</h4>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"n\\">terminate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 函数很简单,如下只进行了 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">giveHandle()</code> 两个操作。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">){</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">()</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">giveHandle</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的,声明如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">WakeUpType</span> <span class=\\"nf\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>在运行状态下,<code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,而默认参数 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code> 为 0,所以会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>这个 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员是一个 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 对象,<code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::wait()</code> 后,会一直等待 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\\"language-plaintext highlighter-rouge\\">wait</code> 的状态。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">set</code> 的动作是由:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::requestHandle()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">PoolThread::push(Poco::AutoPtr&lt;RunnableType&gt;&amp; pRunnable)</code></li>\\n</ul>\\n\\n<p>执行的。</p>\\n\\n<h3 id=\\"二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 同样,继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPManager</span> <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Task</span><span class=\\"p\\">,</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span>\\n</code></pre></div></div>\\n\\n<p>在构造函数中将 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\\"language-plaintext highlighter-rouge\\">_server</code> 引用成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPManager</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">server</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_server</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Task</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPManager\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"cm\\">/* ...... */</span>\\n\\n<span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_server</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的构造函数中调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 成员函数,是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的线程。然后响应到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager::run()</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">PRIO_LOW</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">2000</span><span class=\\"p\\">)</span><span class=\\"o\\">!=</span><span class=\\"n\\">STOP</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">waitHandle</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里要强调的是,这里的 <code class=\\"language-plaintext highlighter-rouge\\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\\n\\n<p>在这里我们可以看到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的主循环的等待时间的。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>你可以自行修改 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 的参数,这样就会调用 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code>)来进行睡眠。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\\"language-plaintext highlighter-rouge\\">handle</code> 成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handle</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_server</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里就会调用到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 源码时知道 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code> 函数并不是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程内运行的,而是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">manage</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\\"language-plaintext highlighter-rouge\\">Session</code>。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</title>\\n \\t<meta name=\\"description\\" content=\\"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</h2>\\t\\t\\n\\t<time datetime=\\"2012-07-23T03:07:43+00:00\\" class=\\"by-line\\">23 Jul 2012, 广州 | 作者 麦克船长 | 总计 3111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1客户端发布publishing-on-client-side\\" id=\\"markdown-toc-1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</a></li>\\n <li><a href=\\"#2服务器端server-side\\" id=\\"markdown-toc-2服务器端server-side\\">2、服务器端(Server-side)</a></li>\\n <li><a href=\\"#3客户端订阅subscribing-on-client-side\\" id=\\"markdown-toc-3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</a></li>\\n <li><a href=\\"#4reference\\" id=\\"markdown-toc-4reference\\">4、Reference</a></li>\\n</ul>\\n\\n<p>整个流程概括如下:</p>\\n\\n<p>Flash 客户端通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 建立连接,然后通过 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\\n\\n<h3 id=\\"1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</h3>\\n\\n<p>通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\\"/2012/04/10/openrtmfp-cumulus-1/\\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\\"language-plaintext highlighter-rouge\\">nc</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost:1935\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\\"language-plaintext highlighter-rouge\\">ns1</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">ns1</span><span class=\\"p\\">.</span><span class=\\"nx\\">publish</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"poechant_media_flow\\"</span><span class=\\"p\\">,</span> <span class=\\"s2\\">\\"live\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\\n\\n<h3 id=\\"2服务器端server-side\\">2、服务器端(Server-side)</h3>\\n\\n<p>Cumulus 通过 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPReceiving</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">rtmfpReceiving</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\\n\\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\\"language-plaintext highlighter-rouge\\">Handshake</code> 类的 <code class=\\"language-plaintext highlighter-rouge\\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ServerSession</span><span class=\\"o\\">::</span><span class=\\"n\\">packetHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Flow</span><span class=\\"o\\">::</span><span class=\\"n\\">fragmentSortedHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">stage</span><span class=\\"p\\">,</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">fragment</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">flags</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF_WITH_HANDLER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">messageHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">amf</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AUDIO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">audioHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">VIDEO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">videoHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">rawHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>接下来在 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushAudioPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushVideoPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushDataPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中的 <code class=\\"language-plaintext highlighter-rouge\\">_listeners</code> 就是该 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Listener</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">addListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">writer</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">unbuffered</span><span class=\\"p\\">);</span>\\n \\n<span class=\\"kt\\">void</span> <span class=\\"nf\\">removeListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这两个函数来实现的。</p>\\n\\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\\n\\n<h3 id=\\"3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</h3>\\n\\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>ns2.play(\\"poechant_media_flow\\");\\n</code></pre></div></div>\\n\\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\\"language-plaintext highlighter-rouge\\">NetStream::send(…)</code> 的,用于发送 <code class=\\"language-plaintext highlighter-rouge\\">Data</code>,<code class=\\"language-plaintext highlighter-rouge\\">Audio</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Video</code> 的程序可以参考该例修改。</p>\\n\\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 初始化的时候,传入 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\\n\\n<h3 id=\\"4reference\\">4、Reference</h3>\\n\\n<ul>\\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\\n</ul>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-25T02:56:26+00:00\\" class=\\"by-line\\">25 Jun 2012, 广州 | 作者 麦克船长 | 总计 2111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中的线程都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>,在其中封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 函数如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样一个类继承 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code>,然后调用到该类自己的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\\"language-plaintext highlighter-rouge\\">SocketManager</code> 为例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">SocketManager</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span> \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>我们要看看这个 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 是怎么回事,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">running</span><span class=\\"p\\">()</span> <span class=\\"k\\">const</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>很简单,就是通过 <code class=\\"language-plaintext highlighter-rouge\\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 是什么时候被设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 的呢?就是上面的 <code class=\\"language-plaintext highlighter-rouge\\">start()</code>,这里存在的一个问题就是先 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 线程,再设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_thread.start(_process);\\n_stop=false;\\n</code></pre></div></div>\\n\\n<p>而 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 之后 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 的时候就开始通过 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 来判断 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值了。所以你会在使用 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>它们是:</p>\\n\\n<ul>\\n <li>主线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程</li>\\n</ul>\\n\\n<p>而异常情况可能是 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动,甚至 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\\" alt=\\"image\\" /></p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动的情况 T.T</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\\n\\n<p>解决办法就是将 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span> \\n <span class=\\"c1\\">// June 25th, 2012, Michael@YY</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-07T15:34:18+00:00\\" class=\\"by-line\\">07 Jun 2012, 广州 | 作者 麦克船长 | 总计 1538 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>OpenRTMFP/Cumulus 提供了 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>。</p>\\n\\n<p>一般来说,Thread A 会准备好要 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息。</p>\\n\\n<p>但是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>由于在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">createAMFMessage</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n \\n <span class=\\"c1\\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"p\\">(</span><span class=\\"n\\">_closed</span> <span class=\\"o\\">||</span> <span class=\\"n\\">signature</span><span class=\\"p\\">.</span><span class=\\"n\\">empty</span><span class=\\"p\\">()</span> <span class=\\"o\\">||</span> <span class=\\"n\\">_band</span><span class=\\"p\\">.</span><span class=\\"n\\">failed</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">MessageBuffered</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"n\\">_MessageNull</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后再调用时最后再增加 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Mutex</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedLock</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">msgQueueMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就基本解决了这个线程安全问题。</p>\\n\\n<p>另外,使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T03:31:10+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 12668 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一流缓冲区\\" id=\\"markdown-toc-一流缓冲区\\">一、流缓冲区</a> <ul>\\n <li><a href=\\"#1了解-stdstreambuf\\" id=\\"markdown-toc-1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></a> <ul>\\n <li><a href=\\"#11单步移动内置指针\\" id=\\"markdown-toc-11单步移动内置指针\\">1.1、单步移动内置指针</a></li>\\n <li><a href=\\"#12获取-get-指针和-put-指针的位置\\" id=\\"markdown-toc-12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</a></li>\\n <li><a href=\\"#13设置-get-和-put-指针可达区域的上下界\\" id=\\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2memorystreambuf\\" id=\\"markdown-toc-2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></a> <ul>\\n <li><a href=\\"#21移动内置的-get-和-put-指针\\" id=\\"markdown-toc-21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</a></li>\\n <li><a href=\\"#22获取-get-和-put-指针当前位置\\" id=\\"markdown-toc-22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</a></li>\\n <li><a href=\\"#23获取缓冲区的起始位置和大小\\" id=\\"markdown-toc-23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</a></li>\\n <li><a href=\\"#24缓冲区的已写字节数\\" id=\\"markdown-toc-24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</a></li>\\n <li><a href=\\"#25显式设定-put-和-get-指针位置\\" id=\\"markdown-toc-25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</a></li>\\n <li><a href=\\"#26-修改缓冲区大小\\" id=\\"markdown-toc-26-修改缓冲区大小\\">2.6 修改缓冲区大小</a></li>\\n <li><a href=\\"#27构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二io-流\\" id=\\"markdown-toc-二io-流\\">二、IO 流</a> <ul>\\n <li><a href=\\"#1了解-stdios\\" id=\\"markdown-toc-1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></a></li>\\n <li><a href=\\"#2memoryios\\" id=\\"markdown-toc-2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></a> <ul>\\n <li><a href=\\"#21构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#22得到-memorystreambuf-成员的地址\\" id=\\"markdown-toc-22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</a></li>\\n <li><a href=\\"#23当前位置\\" id=\\"markdown-toc-23当前位置\\">2.3、当前位置</a></li>\\n <li><a href=\\"#24封装-memorystreambuf-成员的一些函数\\" id=\\"markdown-toc-24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</a></li>\\n <li><a href=\\"#25-缓冲区可读数据的字节数\\" id=\\"markdown-toc-25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3输入流\\" id=\\"markdown-toc-3输入流\\">3、输入流</a></li>\\n <li><a href=\\"#4输出流\\" id=\\"markdown-toc-4输出流\\">4、输出流</a> <ul>\\n <li><a href=\\"#41-构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#42-读取和设定已写字节数\\" id=\\"markdown-toc-42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</a></li>\\n <li><a href=\\"#43-当前位置\\" id=\\"markdown-toc-43-当前位置\\">4.3 当前位置</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#三局部内存片\\" id=\\"markdown-toc-三局部内存片\\">三、局部内存片</a> <ul>\\n <li><a href=\\"#1构造函数\\" id=\\"markdown-toc-1构造函数\\">1、构造函数</a></li>\\n <li><a href=\\"#2析构函数\\" id=\\"markdown-toc-2析构函数\\">2、析构函数</a></li>\\n <li><a href=\\"#3缓冲区切割\\" id=\\"markdown-toc-3缓冲区切割\\">3、缓冲区切割</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\\n\\n<h3 id=\\"一流缓冲区\\">一、流缓冲区</h3>\\n\\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\\n\\n<h4 id=\\"1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></h4>\\n\\n<p>首先要了解 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 内置了一个 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针和一个 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针。<code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\\"language-plaintext highlighter-rouge\\">g</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">p</code> 就分别表示 get pointer 和 put pointer。</p>\\n\\n<h5 id=\\"11单步移动内置指针\\">1.1、单步移动内置指针</h5>\\n\\n<p>Increase get pointer: Advances the get pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">gbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>Increase put pointer: Advances the put pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">pbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</h5>\\n\\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">gptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">pptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</h5>\\n\\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setg</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gnext</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setp</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<h4 id=\\"2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></h4>\\n\\n<p>类定义:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryStreamBuf</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">streambuf</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">friend</span> <span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span><span class=\\"p\\">;</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">overflow</span><span class=\\"p\\">(</span><span class=\\"n\\">int_type</span> <span class=\\"n\\">c</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">underflow</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">sync</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"k\\">operator</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\\n\\n<h5 id=\\"21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针都移动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">gbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</h5>\\n\\n<p>封装 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">gptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</h5>\\n\\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</h5>\\n\\n<p>读取(其中也可能发生设置操作):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// 已写字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">written</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_written</span><span class=\\"o\\">=</span><span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</h5>\\n\\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Save nb char written</span>\\n \\n <span class=\\"c1\\">// 移动 put 指针</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 移动 get 指针</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"26-修改缓冲区大小\\">2.6 修改缓冲区大小</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// 大小标识</span>\\n <span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// gptr 当前位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span> \\n <span class=\\"c1\\">// pptr 当前位置</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>构造函数会设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">bufferSize</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数会拷贝对方的 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code>、<code class=\\"language-plaintext highlighter-rouge\\">bufferSizse</code>、<code class=\\"language-plaintext highlighter-rouge\\">_written</code>,并设定 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>、<code class=\\"language-plaintext highlighter-rouge\\">pptr</code>。注意设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 时,要分别调用 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pbump</code>,因为 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 仅将 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">(),</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">));</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二io-流\\">二、IO 流</h3>\\n\\n<h4 id=\\"1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></h4>\\n\\n<p>Initialize object [<code class=\\"language-plaintext highlighter-rouge\\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">init</span> <span class=\\"p\\">(</span> <span class=\\"n\\">streambuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">sb</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>初始化后如下函数的返回值:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>member function</th>\\n <th>value</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>rdbuf()</td>\\n <td>sb</td>\\n </tr>\\n <tr>\\n <td>tie()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>rdstate()</td>\\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\\n </tr>\\n <tr>\\n <td>exceptions()</td>\\n <td>goodbit</td>\\n </tr>\\n <tr>\\n <td>flags()</td>\\n <td>skipws | dec</td>\\n </tr>\\n <tr>\\n <td>width()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>precision()</td>\\n <td>6</td>\\n </tr>\\n <tr>\\n <td>fill()</td>\\n <td>‘ ’ (whitespace)</td>\\n </tr>\\n <tr>\\n <td>getloc()</td>\\n <td>a copy of locale()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code>,且是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryIOS</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"k\\">virtual</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ios</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span> <span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">poco_ios_init</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">init</code> 的宏定义,用于初始化成员 <code class=\\"language-plaintext highlighter-rouge\\">_buf</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_buf</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23当前位置\\">2.3、当前位置</h5>\\n\\n<p>这是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutpuStream</code> 继承时实现:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code> 封装为 <code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"p\\">(</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">());</span> <span class=\\"c1\\">// 缓冲区剩余可读数据字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">result</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3输入流\\">3、输入流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryInputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryInputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for reading.</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 以及 <code class=\\"language-plaintext highlighter-rouge\\">istream</code>。<code class=\\"language-plaintext highlighter-rouge\\">istream</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">iostream</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">basic_istream</code> 别名(<code class=\\"language-plaintext highlighter-rouge\\">typedef</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"k\\">const_cast</span><span class=\\"o\\">&lt;</span><span class=\\"kt\\">char</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>唯一的一个成员函数是 <code class=\\"language-plaintext highlighter-rouge\\">current</code>,封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的 <code class=\\"language-plaintext highlighter-rouge\\">gCurrent</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4输出流\\">4、输出流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryOutputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span>\\n <span class=\\"c1\\">/// An input stream for reading from a memory area.</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryOutputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for writing.</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>如下,不赘述了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</h5>\\n\\n<p>读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"43-当前位置\\">4.3 当前位置</h5>\\n\\n<p>与 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 中的封装类似:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三局部内存片\\">三、局部内存片</h3>\\n\\n<p>在第一部分的流缓冲区介绍 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 中有一个 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"1构造函数\\">1、构造函数</h4>\\n\\n<p>构造函数传入的参数对应的就是 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">(</span><span class=\\"n\\">offset</span><span class=\\"p\\">),</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">(</span><span class=\\"n\\">buffer</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&gt;=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2析构函数\\">2、析构函数</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3缓冲区切割\\">3、缓冲区切割</h4>\\n\\n<p>可以看到构造函数和析构函数中都调用了 <code class=\\"language-plaintext highlighter-rouge\\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\\n\\n<ul>\\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 获取到 gptr</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gpos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"c1\\">// 偏移缓冲区地址,并修改缓冲区大小</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">ppos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">gpos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">ppos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T02:04:55+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 30820 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一amf-数据类型定义\\" id=\\"markdown-toc-一amf-数据类型定义\\">一、AMF 数据类型定义</a> <ul>\\n <li><a href=\\"#1数据类型\\" id=\\"markdown-toc-1数据类型\\">1、数据类型</a></li>\\n <li><a href=\\"#2undefined-type\\" id=\\"markdown-toc-2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</a></li>\\n <li><a href=\\"#3null-type\\" id=\\"markdown-toc-3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</a></li>\\n <li><a href=\\"#4false-type\\" id=\\"markdown-toc-4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</a></li>\\n <li><a href=\\"#5true-type\\" id=\\"markdown-toc-5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</a></li>\\n <li><a href=\\"#6integer-type\\" id=\\"markdown-toc-6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</a></li>\\n <li><a href=\\"#7double-type\\" id=\\"markdown-toc-7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</a></li>\\n <li><a href=\\"#8string-type\\" id=\\"markdown-toc-8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</a></li>\\n <li><a href=\\"#9xmldocument-type\\" id=\\"markdown-toc-9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</a></li>\\n <li><a href=\\"#10date-type\\" id=\\"markdown-toc-10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</a></li>\\n <li><a href=\\"#11array-type\\" id=\\"markdown-toc-11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</a></li>\\n <li><a href=\\"#12object-type\\" id=\\"markdown-toc-12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</a></li>\\n <li><a href=\\"#13xml-type\\" id=\\"markdown-toc-13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</a></li>\\n <li><a href=\\"#14bytearray-type\\" id=\\"markdown-toc-14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</a></li>\\n <li><a href=\\"#15amf3-的使用\\" id=\\"markdown-toc-15amf3-的使用\\">15、AMF3 的使用</a> <ul>\\n <li><a href=\\"#151netconnection-and-amf-3\\" id=\\"markdown-toc-151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</a></li>\\n <li><a href=\\"#152netconnection-in-actionscript-30\\" id=\\"markdown-toc-152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</a></li>\\n <li><a href=\\"#153bytearray-idatainput-and-idataoutput\\" id=\\"markdown-toc-153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二binaryreaderwriter\\" id=\\"markdown-toc-二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></a> <ul>\\n <li><a href=\\"#1amf3-数据格式基础\\" id=\\"markdown-toc-1amf3-数据格式基础\\">1、AMF3 数据格式基础</a></li>\\n <li><a href=\\"#2序列化\\" id=\\"markdown-toc-2序列化\\">2、序列化</a></li>\\n <li><a href=\\"#3反序列化\\" id=\\"markdown-toc-3反序列化\\">3、反序列化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三packetreaderwriter\\" id=\\"markdown-toc-三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></a> <ul>\\n <li><a href=\\"#1packetreader\\" id=\\"markdown-toc-1packetreader\\">1、PacketReader</a> <ul>\\n <li><a href=\\"#11封装-memoryinputstream\\" id=\\"markdown-toc-11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></a></li>\\n <li><a href=\\"#12收缩缓冲区\\" id=\\"markdown-toc-12收缩缓冲区\\">1.2、收缩缓冲区</a></li>\\n <li><a href=\\"#13构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-13构造函数拷贝构造函数和析构函数\\">1.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2packetwriter\\" id=\\"markdown-toc-2packetwriter\\">2、<code class=\\"language-plaintext highlighter-rouge\\">PacketWriter</code></a> <ul>\\n <li><a href=\\"#21封装memoryoutputstream\\" id=\\"markdown-toc-21封装memoryoutputstream\\">2.1、封装<code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code></a></li>\\n <li><a href=\\"#22封装-binarywriter\\" id=\\"markdown-toc-22封装-binarywriter\\">2.2、封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code></a></li>\\n <li><a href=\\"#23构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-23构造函数拷贝构造函数和析构函数\\">2.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四amfreader\\" id=\\"markdown-toc-四amfreader\\">四、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code></a> <ul>\\n <li><a href=\\"#1objectdef\\" id=\\"markdown-toc-1objectdef\\">1、<code class=\\"language-plaintext highlighter-rouge\\">ObjectDef</code></a></li>\\n <li><a href=\\"#2amfreader-定义\\" id=\\"markdown-toc-2amfreader-定义\\">2、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 定义</a> <ul>\\n <li><a href=\\"#21构造函数析构函数\\" id=\\"markdown-toc-21构造函数析构函数\\">2.1、构造函数、析构函数</a></li>\\n <li><a href=\\"#22简单封装-packetreader-的一些函数\\" id=\\"markdown-toc-22简单封装-packetreader-的一些函数\\">2.2、简单封装 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 的一些函数</a></li>\\n <li><a href=\\"#23设置-gptr-位置\\" id=\\"markdown-toc-23设置-gptr-位置\\">2.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 位置</a></li>\\n <li><a href=\\"#24判断类型\\" id=\\"markdown-toc-24判断类型\\">2.4、判断类型</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3解析-as3-null\\" id=\\"markdown-toc-3解析-as3-null\\">3、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Null</code></a></li>\\n <li><a href=\\"#4解析-as3-number\\" id=\\"markdown-toc-4解析-as3-number\\">4、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Number</code></a></li>\\n <li><a href=\\"#5解析-as3-integer\\" id=\\"markdown-toc-5解析-as3-integer\\">5、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code></a></li>\\n <li><a href=\\"#6解析-as3-boolean\\" id=\\"markdown-toc-6解析-as3-boolean\\">6、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code></a></li>\\n <li><a href=\\"#7开始引用与结束引用\\" id=\\"markdown-toc-7开始引用与结束引用\\">7、开始引用与结束引用</a></li>\\n <li><a href=\\"#8解析-as3-bytearray\\" id=\\"markdown-toc-8解析-as3-bytearray\\">8、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code></a></li>\\n <li><a href=\\"#9解析-as3-date\\" id=\\"markdown-toc-9解析-as3-date\\">9、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Date</code></a></li>\\n <li><a href=\\"#10解析-as3-dictionary\\" id=\\"markdown-toc-10解析-as3-dictionary\\">10、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Dictionary</code></a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。</p>\\n\\n<h3 id=\\"一amf-数据类型定义\\">一、AMF 数据类型定义</h3>\\n\\n<h4 id=\\"1数据类型\\">1、数据类型</h4>\\n\\n<p>各种数据类型的标示都在 AMF.h 中定义为宏</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cp\\">#define AMF_NUMBER 0x00 // 浮点数\\n#define AMF_BOOLEAN 0x01 // 布尔型\\n#define AMF_STRING 0x02 // 字符串\\n#define AMF_BEGIN_OBJECT 0x03 // 对象,开始\\n#define AMF_NULL 0x05 // null\\n#define AMF_UNDEFINED 0x06\\n#define AMF_REFERENCE 0x07\\n#define AMF_MIXED_ARRAY 0x08\\n#define AMF_END_OBJECT 0x09 // 对象,结束\\n#define AMF_BEGIN_TYPED_OBJECT 0x10\\n#define AMF_STRICT_ARRAY 0x0A\\n#define AMF_DATE 0x0B // 日期\\n#define AMF_LONG_STRING 0x0C // 字符串\\n#define AMF_UNSUPPORTED 0x0D\\n</span> \\n<span class=\\"cp\\">#define AMF_AVMPLUS_OBJECT 0x11\\n#define AMF_END 0xFF\\n</span> \\n<span class=\\"cp\\">#define AMF3_UNDEFINED 0x00\\n#define AMF3_NULL 0x01\\n#define AMF3_FALSE 0x02\\n#define AMF3_TRUE 0x03\\n#define AMF3_INTEGER 0x04\\n#define AMF3_NUMBER 0x05\\n#define AMF3_STRING 0x06\\n#define AMF3_DATE 0x08\\n#define AMF3_ARRAY 0x09\\n#define AMF3_OBJECT 0x0A\\n#define AMF3_BYTEARRAY 0x0C\\n#define AMF3_DICTIONARY 0x11\\n</span></code></pre></div></div>\\n\\n<p>并定义了一个枚举类表示数据类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">AMF</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"k\\">enum</span> <span class=\\"n\\">Type</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Null</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Boolean</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Integer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Number</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">String</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Date</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Array</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Object</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ByteArray</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Dictionary</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RawObjectContent</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">End</span>\\n <span class=\\"p\\">};</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型标记表示,用于编码布尔值 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</h4>\\n\\n<p>true 类型由 true 类型标记表示,用于编码布尔值 true。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</h4>\\n\\n<p>在 AMF 3 中,整数使用可变长度的无符号 29 位整数进行序列化。ActionScript 3.0 中的整数类型 - 有符号 <code class=\\"language-plaintext highlighter-rouge\\">int</code> 类型和无符号 <code class=\\"language-plaintext highlighter-rouge\\">uint</code> 类型 - 也使用 29 位在 AVM+中表示。如果无符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">uint</code>) 的值大于等于 229 或者如果有符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">int</code>) 的值大于等于 228,则它将被 AVM+ 表示为 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型,并使用 AMF 3 double 类型进行序列化。</p>\\n\\n<h4 id=\\"7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</h4>\\n\\n<p>AMF 3 的 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型与 AMF 0 的 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 类型编码方式相同。此类型用于编码 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 或值大于等于 228 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">int</code> 或值大于等于 229 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">uint</code>。编码值始终是网络字节顺序中的 8 字节 IEEE-754 双精度浮点值 (低内存中的符号位)。</p>\\n\\n<h4 id=\\"8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</h4>\\n\\n<p>ActionScript String 值使用 AMF 3 中的单个 string 类型表示 - AMF 0 中的 <code class=\\"language-plaintext highlighter-rouge\\">string</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">long string</code> 类型的概念不再使用。可以使用对隐式字符串引用表中的索引将字符串作为先前发生的字符串的引用发送。字符串使用 UTF-8 编码 - 但是头可以描述字符串文本或字符串引用。空字符串永远不会作为引用发送。</p>\\n\\n<h4 id=\\"9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了新的 XML 类型 (参见 3.13),但是旧版的 XMLDocument 类型在语言中被保留为 <code class=\\"language-plaintext highlighter-rouge\\">flash.xml.XMLDocument</code>。与 AMF 0 类似,<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 的结构需要扁平化为字符串表示以进行序列化。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。XMLDocuments 可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</h4>\\n\\n<p>在 AMF 3 中,ActionScript Date 简单地作为自 1970 年 1 月 1 日午夜 (UTC 时区) 以来的毫秒数进行序列化。不发送本地时区信息。可以使用对隐式对象引用表中的索引将日期作为先前发生的日期实例的引用发送。</p>\\n\\n<h4 id=\\"11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</h4>\\n\\n<p>ActionScript 数组的类型和在数组中的位置是基于它们的索引性质描述的。以下表格概述了这些术语的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">strict</code>:仅包含序数(数字)索引</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">dense</code>:序数索引从 0 开始,并且在连续索引之间不存在间隙(即,从 0 到数组长度的每一个索引都被定义了)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">sparse</code>:包含至少两个索引之间的一个间隙</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">associative</code>:包含至少一个非序数(字符串)索引(有时称为 ECMA 数组)</li>\\n</ul>\\n\\n<p>AMF 将数组分为两部分,密集部分和关联部分。关联部分的二进制表示由名称/值对(可能没有)终止的空字符串。密集部分的二进制表示由密集部分的大小(可能为零)以及有序的值列表(可能没有)组成。在 AMF 中写入的顺序是密集部分的大小,一个以空字符串终止的名称/值对列表,然后是大小的值。数组可以通过使用隐式对象引用表的索引作为先前发生的数组的引用来发送。</p>\\n\\n<h4 id=\\"12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</h4>\\n\\n<p>AMF 3 中有一种类型用于处理 ActionScript 对象和自定义用户类。使用术语 “traits” 来描述类的定义特征。除了 “anonymous” 对象和 “typed” 对象,ActionScript 3.0 还引入了两个进一步的 traits 来描述如何序列化对象,即 “dynamic” 和 “externalizable”。以下表格概述了这些术语和它们的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Anonymous</code>:实际的 ActionScript 对象类型的实例或没有注册别名的类的实例(在反序列化时将其视为对象)。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Typed</code>:具有注册别名的类的实例。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Dynamic</code>:具有动态特征声明的类定义的实例;可以在运行时动态地从实例中添加和删除公共变量成员。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Externalizable</code>:实现 flash.utils.IExternalizable 的类的实例,它完全控制其成员的序列化(特征信息中不包含属性名)。</li>\\n</ul>\\n\\n<p>在这些特征之外,对象的特征信息还可能包括在类上定义的一组公共变量和公共可读写属性名称(即不是函数的公共成员)。成员名称的顺序很重要,因为在特征信息之后的成员值将按照完全相同的顺序出现。这些成员被视为密封成员,因为它们是由类型明确定义的。</p>\\n\\n<p>如果类型是动态的,则在密封成员之后可以包括一个进一步的部分,该部分将动态成员列为名称/值对。当遇到空字符串名称时,继续读取动态成员。</p>\\n\\n<p>对象可以通过使用隐式对象引用表中的索引来作为先前发生对象的引用。此外,还可以通过使用隐式特征引用表中的索引将特征信息发送为先前发生的一组特征的引用。</p>\\n\\n<h4 id=\\"13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了一种新的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型,支持 E4X 语法。为了序列化,需要将 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型展平成字符串表示形式。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。<code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 XML 实例的引用发送。请注意,这种编码对 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 的使用造成了一些理论限制。每个 UTF-8 编码的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例的字节长度最大为 228-1 字节(大约 256 MB)。</p>\\n\\n<h4 id=\\"14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</h4>\\n\\n<p>用于保存字节数组,即 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>。AMF 3 使用可变长度编码 29 位整数序列化此类型,其中包括字节长度前缀,然后是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 的原始字节。<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"15amf3-的使用\\">15、AMF3 的使用</h4>\\n\\n<h5 id=\\"151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</h5>\\n\\n<p>除了序列化 ActionScript 类型外,AMF 还可用于远程服务的异步调用。可使用简单的消息结构将一批请求发送到远程端点。此消息结构的格式为 AMF 0(参见[AMF0])。可以使用特殊的 <code class=\\"language-plaintext highlighter-rouge\\">avmplus-object-marker</code> 类型将上下文头值或消息正文切换到 AMF 3 编码。</p>\\n\\n<h5 id=\\"152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</h5>\\n\\n<p>在 ActionScript 3.0 中,NetConnection 的限定类名是 flash.net.NetConnection。这个类仍然使用响应器来处理远程端点的结果和状态响应,但是现在需要强类型的 Responder 类。完全限定的类名是 flash.net.Responder。除了正常的结果和状态响应之外,NetConnection 还会分发事件,开发人员可以添加监听器。下面是这些事件的概述:</p>\\n\\n<ul>\\n <li>当异常异步抛出时触发,例如来自本机异步代码。</li>\\n <li>当输入或输出错误导致网络操作失败时触发。</li>\\n <li>当 NetConnection 对象报告其状态或错误条件时触发。</li>\\n <li>如果对 NetConnection.call() 的调用尝试连接到调用者安全沙箱外的服务器,则会触发。</li>\\n</ul>\\n\\n<h5 id=\\"153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></h5>\\n\\n<p>ActionScript 3.0 引入了一种新类型,用于支持以字节数组形式处理原始数据,即 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.ByteArray</code>。为了协助 ActionScript 对象序列化和复制,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实现了 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataInput</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataOutput</code>。这些接口指定了帮助将常见类型写入字节流的实用方法。两个感兴趣的方法是 <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput.writeObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">IDataInput.readObject</code>。这些方法使用 AMF 编码对象。使用的 AMF 版本由 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray.objectEncoding</code> 方法控制,该方法可以设置为 AMF 3 或 AMF 0。枚举类型 <code class=\\"language-plaintext highlighter-rouge\\">flash.net.ObjectEncoding</code> 包含 AMF 版本的常量:分别为 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF0</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF3</code>。</p>\\n\\n<p>请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray.writeObject</code> 使用一个版本的 AMF 对整个对象进行编码。与 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 不同,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 不会从 AMF 0 开始,然后将 <code class=\\"language-plaintext highlighter-rouge\\">objectEncoding</code> 属性设置为 AMF 3 并切换到 AMF 3。还请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 为每个 <code class=\\"language-plaintext highlighter-rouge\\">readObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">writeObject</code> 调用使用新的对象、对象特征和字符串的隐式引用表。</p>\\n\\n<h3 id=\\"二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></h3>\\n\\n<h4 id=\\"1amf3-数据格式基础\\">1、AMF3 数据格式基础</h4>\\n\\n<p>首先介绍一下变长整数(Variable Length Integer),比如 UInt32 如下。</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档,这是一种压缩方式的整数存储,且每一字节都对后面的数据具有预知作用。那么字符串如何处理呢?下面是字符串的处理方式,AMF0 和 AMF3 都才用 UTF-8 编码方式,并做如下压缩处理:</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档。</p>\\n\\n<h4 id=\\"2序列化\\">2、序列化</h4>\\n\\n<p>序列化包括 8 位、16 位、32 位,以及 UTF-8 和 UTF-16(I guess)编码的 String,还有原生数据(Raw Data)、变长无符号整数(Variable Length Unsigned Integer)以及 IP 地址。所谓序列化就是按照指定格式编写各种对象、基础数据类型值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryWriter</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Net</span><span class=\\"o\\">::</span><span class=\\"n\\">SocketAddress</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryWriter</span> <span class=\\"n\\">BinaryWriterNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>请注意其中名为 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriterNull</code> 的成员。构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">writeRaw</code> 是简单地封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter::writeRaw()</code>,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入整数实现如下,用的是从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的重载运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span> \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入变长整数,这段代码含义也一目了然,就是读取变长无符号 32 位整数、64 位整数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">21</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span> <span class=\\"c1\\">// 4 bytes maximum</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">22</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">63</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Can give 10 bytes!</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">shift</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入 IP 地址的两个函数暂略。</p>\\n\\n<h4 id=\\"3反序列化\\">3、反序列化</h4>\\n\\n<p>反序列化就是从指定格式的数据中读出各类型的数据值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryReader</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitEncoded</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString8</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString16</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">read16</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read32</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryReader</span> <span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造与析构函数都很简单:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">)</span> <span class=\\"o\\">:</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取原生数据(Raw Data):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写整数,用的是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter</code> 的重载运算符:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读写整数依旧使用从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt8</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt16</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read16</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read32</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取变长整数,分别针对 <code class=\\"language-plaintext highlighter-rouge\\">UInt32</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">UInt64</code>,要理解 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 的变长整数才能理解这个:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt64</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt64</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></h3>\\n\\n<h4 id=\\"1packetreader\\">1、PacketReader</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define PACKETRECV_SIZE 2048\\nclass PacketReader: public BinaryReader {\\npublic:\\n PacketReader(const Poco::UInt8* buffer,Poco::UInt32 size);\\n PacketReader(PacketReader&amp;);\\n virtual ~PacketReader();\\n const Poco::UInt32 fragments;\\n Poco::UInt32 available(); // 可读字节数\\n Poco::UInt8* current();\\n Poco::UInt32 position(); // 获取当前的相对位置(相对于起始位置的)\\n void reset(Poco::UInt32 newPos = 0); // 设定当前位置\\n void shrink(Poco::UInt32 rest);\\n void next(Poco::UInt32 size);\\nprivate:\\n MemoryInputStream _memory;\\n};\\n</code></pre></div></div>\\n\\n<h6 id=\\"11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">current</code>:当前绝对位置(内存地址)</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code>:当前位置(绝对位置)减去缓冲区起始位置</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"12收缩缓冲区\\">1.2、收缩缓冲区</h6>\\n\\n<p>封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">resize</code>。不过由于前面的 <code class=\\"language-plaintext highlighter-rouge\\">if</code> 语句影响,传给 resize 的参数一定不会大于缓冲区的当前大小。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">shrink</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">rest</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">rest</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">available</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"rest %u more upper than available %u bytes\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">rest</span><span class=\\"p\\">,</span><span class=\\"n\\">available</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">rest</span> <span class=\\"o\\">=</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"o\\">+</span> <span class=\\"n\\">rest</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"13构造函数拷贝构造函数和析构函数\\">1.3、构造函数、拷贝构造函数和析构函数</h6>\\n\\n<p>构造函数先调用父类 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReader</code> 的构造函数,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">fragments</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">_memory</code> 输入流的缓冲区。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">fragments</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"c1\\">// Consctruction by copy</span>\\n<span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">fragments</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">fragments</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">PacketReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2packetwriter\\">2、<code class=\\"language-plaintext highlighter-rouge\\">PacketWriter</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PacketWriter</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">BinaryWriter</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">good</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clear</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">limit</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryOutputStream</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pOther</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"21封装memoryoutputstream\\">2.1、封装<code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">good</code>:不过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code> 也是封装的 <code class=\\"language-plaintext highlighter-rouge\\">std::ostream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">good</code> 函数,True if no error flags are set.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">good</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">good</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">written</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">length</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code>:设置缓冲区的指针位置,即 <code class=\\"language-plaintext highlighter-rouge\\">position</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code>:移动缓冲区指针</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code>:返回缓冲区的起始地址</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">clear</code>:其实就是修改 written 和 position,使得指定位置后面的数据在以后写的时候可以被覆盖,并不是真正的清除。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">clear</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">limit</code>:封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">limit</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">length</span> <span class=\\"o\\">==</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">length</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">length</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Limit '%d' more upper than buffer size '%d' bytes\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">length</span><span class=\\"p\\">,</span><span class=\\"n\\">_size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">length</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">length</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"22封装-binarywriter\\">2.2、封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">flush</code>:封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">flush</code>,不过 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">flush</code> 实际上是从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter</code> 继承而来的。其作用是「Flushes the underlying stream」。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">flush</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_pOther</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_pOther</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">_pOther</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"23构造函数拷贝构造函数和析构函数\\">2.3、构造函数、拷贝构造函数和析构函数</h6>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pOther</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_size</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"c1\\">// Consctruction by copy</span>\\n<span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_pOther</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_size</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>注意析构函数中会进行 <code class=\\"language-plaintext highlighter-rouge\\">flush</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::~</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"四amfreader\\">四、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code></h3>\\n\\n<h4 id=\\"1objectdef\\">1、<code class=\\"language-plaintext highlighter-rouge\\">ObjectDef</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ObjectDef</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span> \\n <span class=\\"n\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">(</span><span class=\\"n\\">amf3</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">dynamic</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">externalizable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">count</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">arrayType</span><span class=\\"p\\">(</span><span class=\\"n\\">arrayType</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">string</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">hardProperties</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reset</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">dynamic</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">externalizable</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">count</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2amfreader-定义\\">2、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 定义</h4>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 作为其成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">AMFReader</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">AMFReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readSimpleObject</span><span class=\\"p\\">(</span><span class=\\"n\\">AMFSimpleObject</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">object</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">read</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">readNumber</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">readInteger</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readBoolean</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">readByteArray</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Timestamp</span> <span class=\\"n\\">readDate</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readObject</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readArray</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readDictionary</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">weakKeys</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readKey</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readItem</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">readRawObjectContent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readNull</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">startReferencing</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">stopReferencing</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">;</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*&gt;</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_stringReferences</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_classDefReferences</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_amf0References</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_amf0Reset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_reset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_amf3</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">_referencing</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数析构函数\\">2.1、构造函数、析构函数</h5>\\n\\n<p>参数为 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code>,会初始化一些成员变量。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">reader</span><span class=\\"p\\">(</span><span class=\\"n\\">reader</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_amf3</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_amf0Reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_referencing</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构时,会逐一释放 <code class=\\"language-plaintext highlighter-rouge\\">_objectDefs</code> 中对象的内存:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMFReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*&gt;::</span><span class=\\"n\\">iterator</span> <span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">delete</span> <span class=\\"o\\">*</span><span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22简单封装-packetreader-的一些函数\\">2.2、简单封装 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code>:操作指针位置</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_reset</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code>:根据当前缓冲区大小和 <code class=\\"language-plaintext highlighter-rouge\\">written</code> 计算得到</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">current</code>:<code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 内存地址</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">*</span><span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23设置-gptr-位置\\">2.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 位置</h5>\\n\\n<p>其实 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 也被影响了,但是在 <code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 中只用 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>。调用构造函数的时候,<code class=\\"language-plaintext highlighter-rouge\\">reset</code> 被设为 0,其后在每次读取数据的时候都会影响 <code class=\\"language-plaintext highlighter-rouge\\">reset</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_reset</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24判断类型\\">2.4、判断类型</h5>\\n\\n<p>分析请看注释:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">followingType</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先 <code class=\\"language-plaintext highlighter-rouge\\">reset</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">back</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">amf3</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>是 AMF0 类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果没有可读数据了,则返回 AMF::End。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">available</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">End</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>开始读了,先读到的表示 AMF 数据类型。要注意的是调用 current 并不改变指针的位置,所以你会在线面看到调用 next。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt8</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_amf3</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF_AVMPLUS_OBJECT</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">available</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">End</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>AMF3 类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>Undefined 和 null 都当做 null。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_UNDEFINED</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_NULL</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>false 和 true 都是 boolean。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_FALSE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_TRUE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_INTEGER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Integer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_NUMBER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">String</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_DATE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Array</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_DICTIONARY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Dictionary</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Object</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_BYTEARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">ByteArray</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>落到 default 手里的话,就跳过这个字节,读取下一个。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unknown AMF3 type %.2x\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>AMF0 类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">switch</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">null</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">null</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_UNDEFINED</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_NULL</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BOOLEAN</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_NUMBER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">long string</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">string</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">string</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_LONG_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">String</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">mixed array</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">strict array</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">array</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_MIXED_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_STRICT_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Array</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_DATE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin object</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">begin typed object</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">object</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BEGIN_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BEGIN_TYPED_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Object</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是引用,就跳过表示类型值的这个字节。这个先留下来,带我们分析完 readArray 和 readObject 再回头看。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_REFERENCE</span><span class=\\"p\\">:</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read16</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">reference</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_amf0References</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF0 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_amf0Reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_amf0References</span><span class=\\"p\\">[</span><span class=\\"n\\">reference</span><span class=\\"p\\">]);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果没了,或者不支持,或者都不是,就跳过这个字节,递归继续读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_END_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF end object type without begin object type before\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_UNSUPPORTED</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unsupported type in AMF format\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">default</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unknown AMF type %.2x\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">followingType</code> 是这个类的核心,每个具体的数据类型的分析都依赖于它的判断。这些类型的解析,会在下一篇文章中介绍。</p>\\n\\n<h4 id=\\"3解析-as3-null\\">3、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Null</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readNull</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先 reset 一下是惯例,就像糗百上的割一样。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span> \\n</code></pre></div></div>\\n\\n<p>AMF 数据类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">Null</code>,跳过该字节,并返回</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>判断错误</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Null type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4解析-as3-number\\">4、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Number</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">double</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readNumber</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Null</code> 会被悲催的跳过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 呀 :(</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Number type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过该字节吧</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>返回吧,返回之前还用到 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 的运算符,注意 AS3 中的 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 就是 C++ 的 <code class=\\"language-plaintext highlighter-rouge\\">double</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5解析-as3-integer\\">5、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Int32</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readInteger</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code> 类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>Null 的话:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code> 或者 Number 的话。。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Integer</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Integer type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过吧。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>终于是 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读一个变长的 32 位无符号整数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Forced in AMF3 here!</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果大于 3.5 个字节所能表示的最大无符号整数值(<code class=\\"language-plaintext highlighter-rouge\\">268435455</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">0xFFFFFFF</code>),则减去 <code class=\\"language-plaintext highlighter-rouge\\">0x2FFFFFFF</code>(这还不是太理解,有能解释的朋友给留个言,或者发 email 给我 )</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">268435455</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">value</span> <span class=\\"o\\">-=</span> <span class=\\"p\\">(</span><span class=\\"mi\\">1</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"mi\\">29</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"6解析-as3-boolean\\">6、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readBoolean</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">Null</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>居然不是 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code> 的话。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Boolean type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 的话,返回 <code class=\\"language-plaintext highlighter-rouge\\">true</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">false</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span><span class=\\"o\\">==</span> <span class=\\"n\\">AMF3_FALSE</span> <span class=\\"o\\">?</span> <span class=\\"nb\\">false</span> <span class=\\"o\\">:</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">AMF0</code> 喽:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span><span class=\\"o\\">==</span><span class=\\"mh\\">0x00</span> <span class=\\"o\\">?</span> <span class=\\"nb\\">false</span> <span class=\\"o\\">:</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"7开始引用与结束引用\\">7、开始引用与结束引用</h4>\\n\\n<p>如下这两个函数会在 <code class=\\"language-plaintext highlighter-rouge\\">FlowConnection</code> 中调用。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">startReferencing</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_referencing</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">stopReferencing</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_referencing</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"8解析-as3-bytearray\\">8、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code></h4>\\n\\n<p>先回顾一下 AMF3 中的ByteArray 的数据格式:</p>\\n\\n<p>注意到,首先要读取一个变长无符号 32 位整数,但是最低位是 1,只有 28 位用于表示数据长度。解释完这里,下面的解析过程才好理解。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readByteArray</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Null</code> 就返回 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReaderNull</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>,也返回 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReaderNull</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">ByteArray</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF ByteArray type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过这个字节:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>注意 position 返回的是相对位置,与 AS3 中一样。<code class=\\"language-plaintext highlighter-rouge\\">reference</code> 表示这个地址(简单说,引用就是地址嘛)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>读取一个变长 32 位无符号整数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>最低位是 1 的话,<code class=\\"language-plaintext highlighter-rouge\\">isInline</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,否则为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>右移一位,因为那一位是标志位,上面解释过了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">isInline</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,表示是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">_referencing</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code> 的话(这是一个 <code class=\\"language-plaintext highlighter-rouge\\">vector</code>),push back this reference:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不符合 ByteArray 的格式定义的话:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">size</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>移动到这个 reference 的位置,<code class=\\"language-plaintext highlighter-rouge\\">_references[size]</code> 就是这个位置(相对)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">size</span><span class=\\"p\\">]);</span> <span class=\\"c1\\">// TODO size 作为索引,还没有完全理解</span>\\n</code></pre></div></div>\\n\\n<p>读取这个 reference 的 size 值给 size对象(注意 size 是这个函数传入的引用参数,其值可以被修改)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>把读取完 ByteArraty 的 PacketReader 返回:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>最后强调一点,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 的数据段最大长度为 228 -1 字节,约为 256 MB。</p>\\n\\n<h4 id=\\"9解析-as3-date\\">9、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Date</code></h4>\\n\\n<p>先看下 <code class=\\"language-plaintext highlighter-rouge\\">Date</code> 的数据格式:</p>\\n\\n<p>下面开始分析:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Timestamp</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readDate</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>Null 的话,就返回当前时间:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是 Date 类型,也返回当前时间:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Date type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是 AMF3:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先读取 flag,最低一位必须是 1,其他位丢到垃圾桶。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">flags</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>当前相对位置。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>是 1 就 push back 到 <code class=\\"language-plaintext highlighter-rouge\\">_references</code> 里。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">flags</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>读取一个 double,到 result 里(result 也是 double 类型哦~)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位不是 1,麻烦不少哒。。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flags</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果 flag 超了,就返回当前时间作为时间戳作为 Date。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">flags</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这段与 ByteArray 那段一样:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">flags</span><span class=\\"p\\">]);</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>返回喽~</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"nf\\">Timestamp</span><span class=\\"p\\">((</span><span class=\\"n\\">Timestamp</span><span class=\\"o\\">::</span><span class=\\"n\\">TimeVal</span><span class=\\"p\\">)</span> <span class=\\"n\\">result</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>读俩,因为是 double(64 位):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">2</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Timezone, useless</span>\\n</code></pre></div></div>\\n\\n<p>返回喽~</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"nf\\">Timestamp</span><span class=\\"p\\">((</span><span class=\\"n\\">Timestamp</span><span class=\\"o\\">::</span><span class=\\"n\\">TimeVal</span><span class=\\"p\\">)</span> <span class=\\"n\\">result</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"10解析-as3-dictionary\\">10、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Dictionary</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readDictionary</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">weakKeys</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>下面这段咱就略了。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Dictionary</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Dictionary type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过 type:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// AMF3</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// marker</span>\\n</code></pre></div></div>\\n\\n<p>当前相对位置值作为 <code class=\\"language-plaintext highlighter-rouge\\">reference</code>,再读个 <code class=\\"language-plaintext highlighter-rouge\\">size</code>,还是最低位必须为 1,不是就返回 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">size</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">isInline</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">size</span><span class=\\"o\\">&gt;</span><span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>下面要调用到 <code class=\\"language-plaintext highlighter-rouge\\">ObjectRef</code> 构造函数,这里再把其实现拿出来看看,其实主要是初始化了哪些成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">(</span><span class=\\"n\\">amf3</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">dynamic</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">externalizable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">count</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">arrayType</span><span class=\\"p\\">(</span><span class=\\"n\\">arrayType</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>可以看到要有一个 amf3,还有 <code class=\\"language-plaintext highlighter-rouge\\">reset</code> 置为 0,<code class=\\"language-plaintext highlighter-rouge\\">dynamic</code> 置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,<code class=\\"language-plaintext highlighter-rouge\\">externalizable</code> 也是 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,<code class=\\"language-plaintext highlighter-rouge\\">count</code> 是 0,<code class=\\"language-plaintext highlighter-rouge\\">arrayType</code> 成员要赋值。</p>\\n\\n<p>上面是插播哦,下面还要继续哒。创建这么一个对象,注意是 new 出来的,所以我们在《OpenRTMFP/Cumulus Primer(16)AMF解析之AMFReader》一文中提到了 AMFReader 的析构函数中要对 <code class=\\"language-plaintext highlighter-rouge\\">_objectRef</code> 的每个元素逐一析构的。<code class=\\"language-plaintext highlighter-rouge\\">arrayType</code> 就设置为 <code class=\\"language-plaintext highlighter-rouge\\">AMF3_DICTIONARY</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*</span> <span class=\\"n\\">pObjectDef</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nf\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">,</span> <span class=\\"n\\">AMF3_DICTIONARY</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">dynamic</span><span class=\\"o\\">=</span><span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pObjectDef</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位是 1,就直接 push back,跟之前一样。不过这里多了一个 <code class=\\"language-plaintext highlighter-rouge\\">pObjectDef</code>,所以还要设置一下它的计数为 <code class=\\"language-plaintext highlighter-rouge\\">size</code>,就是 <code class=\\"language-plaintext highlighter-rouge\\">dictionary</code> 数据大小。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位是 0,就把 <code class=\\"language-plaintext highlighter-rouge\\">count</code> 设置为下一个变长整数值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">size</span><span class=\\"p\\">]);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">*=</span> <span class=\\"mi\\">2</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>读一个字节,如果最小位是 1,weakKeys 就是 true,否则为 false。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">weakKeys</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">return</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</title>\\n \\t<meta name=\\"description\\" content=\\"CumulusServer 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 CumulusEdge 和 CumulusServer 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 CumulusServer 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-15T14:26:58+00:00\\" class=\\"by-line\\">15 Apr 2012, 广州 | 作者 麦克船长 | 总计 3844 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1绑定地址\\" id=\\"markdown-toc-1绑定地址\\">1、绑定地址</a></li>\\n <li><a href=\\"#2cumulusserver-接收数据\\" id=\\"markdown-toc-2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</a></li>\\n <li><a href=\\"#3如果-cumulusedge-端口存在且-edge-socket-可用\\" id=\\"markdown-toc-3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</a></li>\\n <li><a href=\\"#4cumulusserver-和-cumulusedge-的-socket-都没有数据\\" id=\\"markdown-toc-4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</a></li>\\n <li><a href=\\"#5发送方的-ip-被禁\\" id=\\"markdown-toc-5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</a></li>\\n <li><a href=\\"#6数据包长度小于可能的最小值12\\" id=\\"markdown-toc-6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</a></li>\\n</ul>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。</p>\\n\\n<p>本所要介绍的这个主循环在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(const volatile bool&amp; terminate)</code> 函数中。RTMFPServer覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"1绑定地址\\">1、绑定地址</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">address</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">绑定</span><span class=\\"n\\">CumulusEdge</span><span class=\\"err\\">的</span> <span class=\\"n\\">IP</span> <span class=\\"err\\">地址和端口:</span>\\n\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">edgesAddress</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>发送者(Client)的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"n\\">sender</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">buff</span><span class=\\"p\\">[</span><span class=\\"n\\">PACKETRECV_SIZE</span><span class=\\"p\\">];</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">stop</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">idle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">realTime</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 读取:把数据存到 <code class=\\"language-plaintext highlighter-rouge\\">buff</code>,把发送者地址赋给 <code class=\\"language-plaintext highlighter-rouge\\">sender</code>,把所读长度返回给 <code class=\\"language-plaintext highlighter-rouge\\">size</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>处理 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span> <span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span> <span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span> <span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">Edge</span><span class=\\"o\\">*</span> <span class=\\"n\\">pEdge</span> <span class=\\"o\\">=</span> <span class=\\"n\\">edges</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pEdge</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pEdge</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 空闲:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">idle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>主线程等待一秒。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">isElapsed</span><span class=\\"p\\">(</span><span class=\\"n\\">_freqManage</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>Just middle session</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Sessions</span><span class=\\"o\\">::</span><span class=\\"n\\">Iterator</span> <span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Middle</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMiddle</span> <span class=\\"o\\">=</span> <span class=\\"k\\">dynamic_cast</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Middle</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMiddle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pMiddle</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isBanned</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">INFO</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Data rejected because client %s is banned\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">().</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">size</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">RTMFP_MIN_PACKET_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Invalid packet\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">PacketReader</span> <span class=\\"nf\\">packet</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Session</span><span class=\\"o\\">*</span> <span class=\\"n\\">pSession</span> <span class=\\"o\\">=</span> <span class=\\"n\\">findSession</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFP</span><span class=\\"o\\">::</span><span class=\\"n\\">Unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">));</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">checked</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">commitCookie</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pSession</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>给 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 或者自己(<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>)的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">setEndPoint</span><span class=\\"p\\">(</span><span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">?</span> <span class=\\"n\\">_edgesSocket</span> <span class=\\"o\\">:</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">,</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">delete</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的第二篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-14T11:20:46+00:00\\" class=\\"by-line\\">14 Apr 2012, 广州 | 作者 麦克船长 | 总计 18890 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一cumulus-启动源码分析\\" id=\\"markdown-toc-一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数\\" id=\\"markdown-toc-1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</a></li>\\n <li><a href=\\"#2maincpp-中的-cumulusserver-构造函数\\" id=\\"markdown-toc-2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</a></li>\\n <li><a href=\\"#3maincpp-中的-cumulusserver-的-initialize-成员函数\\" id=\\"markdown-toc-3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</a></li>\\n <li><a href=\\"#4maincpp-中的-cumulusserver-的-main-成员函数\\" id=\\"markdown-toc-4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</a></li>\\n <li><a href=\\"#5cumulusserver-是-serverapplication-的子类\\" id=\\"markdown-toc-5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</a></li>\\n <li><a href=\\"#6serverapplication-是-application-的子类\\" id=\\"markdown-toc-6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</a></li>\\n <li><a href=\\"#7反初始化\\" id=\\"markdown-toc-7反初始化\\">7、反初始化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二cumulusserver-各项交互功能的源码解读\\" id=\\"markdown-toc-二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</a> <ul>\\n <li><a href=\\"#1命令行选项设定\\" id=\\"markdown-toc-1命令行选项设定\\">1、命令行选项设定</a></li>\\n <li><a href=\\"#2处理命令行选项\\" id=\\"markdown-toc-2处理命令行选项\\">2、处理命令行选项</a></li>\\n <li><a href=\\"#6dump-logs\\" id=\\"markdown-toc-6dump-logs\\">6、Dump logs</a></li>\\n <li><a href=\\"#3停止运行\\" id=\\"markdown-toc-3停止运行\\">3、停止运行</a></li>\\n <li><a href=\\"#4载入配置\\" id=\\"markdown-toc-4载入配置\\">4、载入配置</a></li>\\n <li><a href=\\"#5处理日志\\" id=\\"markdown-toc-5处理日志\\">5、处理日志</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三maincpp-的-main-函数源码分析\\" id=\\"markdown-toc-三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数中的-server\\" id=\\"markdown-toc-1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></a></li>\\n <li><a href=\\"#2maincpp-中-main-函数的-serverstart\\" id=\\"markdown-toc-2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></a></li>\\n <li><a href=\\"#3回顾一下整个启动流程\\" id=\\"markdown-toc-3回顾一下整个启动流程\\">3、回顾一下整个启动流程</a></li>\\n <li><a href=\\"#4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\" id=\\"markdown-toc-4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。首先要知道的是,OpenRTMFP/Cumulus 中使用到的库有 Poco、OpenSSL 和 Lua。</p>\\n\\n<h3 id=\\"一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</h4>\\n\\n<p>入口在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"kt\\">int</span> <span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">argv</span><span class=\\"p\\">[])</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先检查内存泄露,不过目前这个开发中的项目还没有实现这个功能,只是个空函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">DetectMemoryLeak</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后会创建一个匿名的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象,并调用其 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,该函数由 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 从 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 中继承而来,而 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 由是从 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 继承而来的。<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象调用 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,实际是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是调用 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的函数,而该 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。如果 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数被调用时抛出异常,则不会执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,但仍然会执行 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Runs the application by performing additional initializations</span>\\n <span class=\\"c1\\">// and calling the main() method.</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">CumulusServer</span><span class=\\"p\\">().</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"n\\">argv</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">()</span><span class=\\"o\\">:</span> <span class=\\"n\\">_helpRequested</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span> <span class=\\"c1\\">// 显示帮助信息</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_middle</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_isInteractive</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pLogFile</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</h4>\\n\\n<p>在执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数之前,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 会启动 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,传入的参数就是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 自己,可以猜到 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run</code> 方法中,调用该函数时的参数是 <code class=\\"language-plaintext highlighter-rouge\\">this</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">Application</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">self</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>调用父函数 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的初始化函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">self</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>再继续下面的源码分析之前,先要知道,根据 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> ,<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 类有一些内置的配置属性,如下:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.path</code>: 可执行文件的绝对路径;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.name</code>: 可执行文件的文件名(含扩展名);</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.baseName</code>: 可执行文件的文件名(不含扩展名)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.dir</code>: 可执行文件的所在目录;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.configDir</code>: 配置文件所在目录;</li>\\n</ul>\\n\\n<p>所以下面就读取了可执行文件的所在目录,其中第二个参数表示默认值(即当前目录):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>然后读取配置文件,目录为上一句所得到的 <code class=\\"language-plaintext highlighter-rouge\\">dir</code>,文件名(不含扩展名)为 <code class=\\"language-plaintext highlighter-rouge\\">application.basename</code> 内置配置属性值,其默认值为 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>,然后加上点和扩展名 <code class=\\"language-plaintext highlighter-rouge\\">.ini</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">dir</span>\\n <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.baseName\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"CumulusServer\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">+</span> <span class=\\"s\\">\\".ini\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这样就加载完配置了。然后查看当前的进程是从命令行运行的(命令行是交互的,所以是 interactive),还是以 daemon 方式运行的,这个函数是ServerApplication的一个成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_isInteractive</span> <span class=\\"o\\">=</span> <span class=\\"n\\">isInteractive</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后获取表示日志文件所在目录的字符串,其中 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code> 是外置配置属性(配置文件中),其默认值为上面获取到的可执行文件路径(一般为当前路径)与 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 的组合,即一般为当前目录下的 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"nf\\">logDir</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.directory\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"logs\\"</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>创建日志文件目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">logDir</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n\\n</code></pre></div></div>\\n\\n<p>日志文件绝对路径,<code class=\\"language-plaintext highlighter-rouge\\">logs</code> 为默认的日志文件名(不含扩展名的部分),扩展名用0:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logDir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span> <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.name\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\".\\"</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pLogFile</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nf\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"0\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>用日志流打开日志文件(方式为追加写入):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Logs</code> 是一个方法类(其中的 <code class=\\"language-plaintext highlighter-rouge\\">public</code> 函数都是静态的),<code class=\\"language-plaintext highlighter-rouge\\">SetLogger</code> 的作用就是将Logs中的似有静态成员设置为某个 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code> 对象(由于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLogger</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</h4>\\n\\n<p>OpenRTMFP/Cumulus 是一个基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的服务端应用(准确的说是基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::ServerApplication</code> 的服务端应用)。如果没有特殊的启动要求,可以调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco/Application.h</code> 中定义的宏 <code class=\\"language-plaintext highlighter-rouge\\">POCO_APP_MAIN</code> 来完成初始化、日志和启动(该宏会根据不同的平台启用不同的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数)。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数在调用完 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数后,会调用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,该 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的定义在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&gt;&amp;</span> <span class=\\"n\\">args</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>首先看是否是要求帮助信息,<code class=\\"language-plaintext highlighter-rouge\\">displayHelp</code> 是借助 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::HelpFormatter</code> 实现的,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数会在调用时将 <code class=\\"language-plaintext highlighter-rouge\\">_helpRequested</code> 设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_helpRequested</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">displayHelp</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是,则进入启动状态,首先创建一个 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServerParams</code> 对象 <code class=\\"language-plaintext highlighter-rouge\\">params</code>,用来存储 OpenRTMFP/CumulusServer 的基本配置信息。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">RTMFPServerParams</span> <span class=\\"n\\">params</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">params</code> 存储 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的端口号和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的端口号:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"port\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RTMFP_DEFAULT_PORT</span><span class=\\"o\\">+</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getBool</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.activated\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port must have a positive value if \\\\\\n edges.activated is true. Server edges is \\\\\\n disactivated.\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">=</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">_pCirrus</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">SocketAddress</code> 的成员,是封装IP地址和端口号的对象。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_middle</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>UDB 所使用的缓冲区大小:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"udpBufferSize\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"keepAliveServer\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"keepAlivePeer\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>失败前 CumulusEdge 的尝试次数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"edges.attemptsBeforeFallback\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> 函数是 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中必须调用的,意为等待终止运行的操作请求。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// wait for CTRL-C or kill</span>\\n <span class=\\"n\\">waitForTerminationRequest</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>一旦接收到终止操作的请求,就会执行下面这句,用以退出 OpenRTMFP/Cumulus 的运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Stop the server</span>\\n <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">stop</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">catch</code> 一些可能产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Configuration problem : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>OpenRTMFP/CumulusServer 停止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"n\\">Application</span><span class=\\"o\\">::</span><span class=\\"n\\">EXIT_OK</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 对其子类有如下要求:</p>\\n\\n<ul>\\n <li>Subsystems must be registered in the constructor.</li>\\n <li>All non-trivial initializations must be made in the <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>` method.</li>\\n <li>At the end of the <code class=\\"language-plaintext highlighter-rouge\\">main()</code> method, <code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> should be called.</li>\\n</ul>\\n\\n<h4 id=\\"6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</h4>\\n\\n<p>Application 对其子类的要求是,如下这些成员函数必须被覆盖:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> (the one-argument, protected variant):上一篇已介绍过。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code>:下面会介绍,Application 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会在调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">reinitialize()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">defineOptions()</code>:定义命令行启动选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">handleOption()</code>:响应相应的命令行选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">main()</code></li>\\n</ul>\\n\\n<h4 id=\\"7反初始化\\">7、反初始化</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的。<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code>,最后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code>。最后这个反初始化过程,在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 就是直接调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">uninitialize</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">uninitialize</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</h3>\\n\\n<h4 id=\\"1命令行选项设定\\">1、命令行选项设定</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的命令行选项有:<code class=\\"language-plaintext highlighter-rouge\\">log(l)</code>、<code class=\\"language-plaintext highlighter-rouge\\">dump(d)</code>、<code class=\\"language-plaintext highlighter-rouge\\">cirrus(c)</code>、<code class=\\"language-plaintext highlighter-rouge\\">middle(m)</code>、<code class=\\"language-plaintext highlighter-rouge\\">help(h)</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">OptionSet</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">options</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>设定日志级别(0 - 8,默认是 6,表示 info 级别)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"l\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Log level argument, must be beetween 0 and 8 : \\\\\\n nothing, fatal, critic, error, warn, note, info, debug, trace. \\\\\\n Default value is 6 (info), all logs until info level are displayed.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">argument</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"level\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>其他一些选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"d\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Enables packet traces in logs. Optional arguments \\\\\\n are 'middle' or 'all' respectively to displays just middle packet \\\\\\n process or all packet process. If no argument is given, just outside \\\\\\n packet process will be dumped.\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"middle|all\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"c\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Cirrus address to activate a 'man-in-the-middle' \\\\\\n developer mode in bypassing flash packets to the official cirrus \\\\\\n server of your choice, it's a instable mode to help Cumulus developers, \\\\\\n </span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\">p2p.rtmfp.net:10000</span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\"> for example. By adding the 'dump' argument, \\\\\\n you will able to display Cirrus/Flash packet exchange in your logs \\\\\\n (see 'dump' argument).\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"address\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"m\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"Enables a 'man-in-the-middle' developer mode \\\\\\n between two peers. It's a instable mode to help Cumulus developers. \\\\\\n By adding the 'dump' argument, you will able to display Flash/Flash \\\\\\n packet exchange in your logs (see 'dump' argument).\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>显示帮助信息的选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"h\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Displays help information about command-line usage.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">OptionSet</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::OptionSet</code>,调用addOption可以向其中增加选项Option。其中required和repeatable表示:</p>\\n\\n<p>Sets whether the option is required (flag == true) or optional (flag == false).\\nReturns true if the option can be specified more than once, or false if at most once.\\n当需要显示帮助信息时,调用如下函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">displayHelp</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">HelpFormatter</span> <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setCommand</span><span class=\\"p\\">(</span><span class=\\"n\\">commandName</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setUsage</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"OPTIONS\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setHeader</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer, open source RTMFP server\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">cout</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setCommand()</code>: Sets the command name.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setUsage()</code>: Sets the usage string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setHeader()</code>: Sets the header string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">format()</code>: Writes the formatted help text to the given stream.</li>\\n</ul>\\n\\n<h4 id=\\"2处理命令行选项\\">2、处理命令行选项</h4>\\n\\n<p>参数是选项名和选项值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是帮助:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_helpRequested</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">URI</span> <span class=\\"n\\">uri</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"rtmfp://\\"</span><span class=\\"o\\">+</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">SocketAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getHost</span><span class=\\"p\\">(),</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getPort</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' : the exchange will bypass to '%s'\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' error : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">message</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是dump日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"all\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">ALL</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">MIDDLE</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">EXTERNAL</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是log,表示设定日志级别:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLevel</span><span class=\\"p\\">(</span><span class=\\"n\\">atoi</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">()));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"6dump-logs\\">6、Dump logs</h4>\\n\\n<p>先加一个作用域锁,然后再向日志流写数据。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">dumpHandler</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">cout</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>调用 manageLogFile,主要做一些日志大小超出限制的处理。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">manageLogFile</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先判断是否超过日志文件的大小上线,LOG_SIZE是1000000字节(即约 1 MB)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">getSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">LOG_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">num</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>打开新日志文件:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span> <span class=\\"nf\\">file</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"10\\"</span><span class=\\"p\\">);</span>\\n\\n</code></pre></div></div>\\n\\n<p>如果该文件已经存在,则先删除:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">remove</span><span class=\\"p\\">();</span>\\n\\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">--</span><span class=\\"n\\">num</span> <span class=\\"o\\">&gt;=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">renameTo</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span> <span class=\\"o\\">+</span> <span class=\\"mi\\">1</span><span class=\\"p\\">));</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span> \\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3停止运行\\">3、停止运行</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code>,该类中有纯虚函数 <code class=\\"language-plaintext highlighter-rouge\\">kill()</code> 需要被实现,于是有:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">kill</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">terminate</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code> 的定义在 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller.h</code> 中,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ApplicationKiller</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n \\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">kill</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4载入配置\\">4、载入配置</h4>\\n\\n<p>在initialize()函数中调用,上一篇已提到过。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">path</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5处理日志\\">5、处理日志</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">logHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">TID</span> <span class=\\"n\\">threadId</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">threadName</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Priority</span> <span class=\\"n\\">priority</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">filePath</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">long</span> <span class=\\"n\\">line</span><span class=\\"p\\">,</span> \\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">text</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>作用域锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">Path</span> <span class=\\"nf\\">path</span><span class=\\"p\\">(</span><span class=\\"n\\">filePath</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">string</span> <span class=\\"n\\">file</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getExtension</span><span class=\\"p\\">()</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"lua\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">directory</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">depth</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是命令行交互模式(即不是 daemon 模式):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_isInteractive</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">printf</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"%s %s[%ld] %s</span><span class=\\"se\\">\\\\n</span><span class=\\"s\\">\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">],</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getBaseName</span><span class=\\"p\\">()).</span><span class=\\"n\\">c_str</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">line</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">text</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>向日志流输出一句日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">DateTimeFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">LocalDateTime</span><span class=\\"p\\">(),</span><span class=\\"s\\">\\"%d/%m %H:%M:%S.%c \\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">]</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'\\\\t'</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadName</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'('</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadId</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\")</span><span class=\\"se\\">\\\\t</span><span class=\\"s\\">\\"</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getFileName</span><span class=\\"p\\">())</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'['</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">line</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\"] \\"</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">text</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">endl</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>日志文件的善后处理(主要处理文件大小限制可能产生的问题):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中真正启动的是 <code class=\\"language-plaintext highlighter-rouge\\">server</code>,它继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code>,而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code> 又继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Gateway</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Handler</code>。而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code> 继承自 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Runnable</code>,所以其是一个可以运行的线程。在 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 中,这是主线程。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span> <span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n<span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/Server.h</code> 中定义的,其构造函数的原型为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>个参数含义如下:</p>\\n\\n<blockquote>\\n <p>The Path Root for the Server Application Killer for Termanting the Server Application Server Configuration</p>\\n</blockquote>\\n\\n<p>距离来说,在我的 Worksapce 中:</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">root</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 构造函数的初始化列表极长:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"o\\">::</span><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">)</span> \\n <span class=\\"o\\">:</span> <span class=\\"n\\">_blacklist</span><span class=\\"p\\">(</span><span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"blacklist\\"</span><span class=\\"p\\">,</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_applicationKiller</span><span class=\\"p\\">(</span><span class=\\"n\\">applicationKiller</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_hasOnRealTime</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pService</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">luaMail</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"o\\">=</span><span class=\\"n\\">Script</span><span class=\\"o\\">::</span><span class=\\"n\\">CreateState</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.host\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"localhost\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">SMTPSession</span><span class=\\"o\\">::</span><span class=\\"n\\">SMTP_PORT</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.timeout\\"</span><span class=\\"p\\">,</span><span class=\\"mi\\">60</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>下面调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::File</code> 创建目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">((</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">WWWPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"www\\"</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>因为 <code class=\\"language-plaintext highlighter-rouge\\">roor</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 目录,所以 <code class=\\"language-plaintext highlighter-rouge\\">WWWPath</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/www</code> 目录。然后初始化 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code>,这个 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code> 是和 Lua 有关的东东,这里暂不细说,先知道与 Lua 相关就好。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Service</span><span class=\\"o\\">::</span><span class=\\"n\\">InitGlobalTable</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>下面就涉及到了 Lua script 了:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SCRIPT_BEGIN</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">SCRIPT_CREATE_PERSISTENT_OBJECT</span><span class=\\"p\\">(</span><span class=\\"n\\">Invoker</span><span class=\\"p\\">,</span><span class=\\"n\\">LUAInvoker</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">readNextConfig</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"n\\">configurations</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">lua_setglobal</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"cumulus.configs\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">SCRIPT_END</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN</code>、<code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_CREATE_PERSISTENT_OBJECT和SCRIPT_END</code> 都是宏,其定义在 <code class=\\"language-plaintext highlighter-rouge\\">Script.h</code> 文件中,如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define SCRIPT_BEGIN(STATE) \\\\\\n if (lua_State* __pState = STATE) { \\\\\\n const char* __error=NULL;\\n \\n#define SCRIPT_CREATE_PERSISTENT_OBJECT(TYPE,LUATYPE,OBJ) \\\\\\n Script::WritePersistentObject&lt;TYPE,LUATYPE&gt;(__pState,OBJ); \\\\\\n lua_pop(__pState,1);\\n \\n#define SCRIPT_END }\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN和SCRIPT_END</code> 经常用到,当与 Lua 相关的操作出现时,都会以这两个宏作为开头和结尾。</p>\\n\\n<h4 id=\\"2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServerParams</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">params</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 正在运行,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer server is yet running, call stop method before\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定端口号,如果端口号为 0,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must have a positive value\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusEdge</code> 的端口号,如果其端口号与 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusSever</code> 端口号相同,则返回并终止启动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"n\\">_edgesPort</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must different than RTMFPServer edges.port\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>Cirrus:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">2000000</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// 2 sec by default</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">Target</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// no waiting, direct process in the middle case!</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode with server %s \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pCirrus</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">address</span><span class=\\"p\\">.</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode between peers \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>UDP Buffer:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span> <span class=\\"o\\">?</span> \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">getReceiveBufferSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Socket buffer receving/sending size = %u/%u\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">threadPriority</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>启动线程,进入循环运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>上句具体的源码实现为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果在运行则返回并终止启动。然后加一个局部锁。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果不得不join()到主线程中,那就join()吧</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后就运行这个线程吧:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3回顾一下整个启动流程\\">3、回顾一下整个启动流程</h4>\\n\\n<p>到此我们先回顾一下启动过程:</p>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的启动入口 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数开始,创建 <code class=\\"language-plaintext highlighter-rouge\\">Server</code> 对象并启动(调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 函数)。<code class=\\"language-plaintext highlighter-rouge\\">Server::start()</code> 中调用其父类(<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code>)的父类(<code class=\\"language-plaintext highlighter-rouge\\">Startable</code>)的方法 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 开启线程。\\n调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 函数后,开启线城时传入的参数为 <code class=\\"language-plaintext highlighter-rouge\\">*this</code>,所以就会运行 <code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code>;</p>\\n\\n<h4 id=\\"4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code> 调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,但这个函数被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖,所以会运行 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>,其源码如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果CumulusEdge:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP edges server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">onStart</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>运行线程:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>处理异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果跳出了,则终止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">onStop</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server stops\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>该函数内部又会调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,该函数调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>它是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 实现。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 会调用 <code class=\\"language-plaintext highlighter-rouge\\">void run(const volatile bool&amp; terminate)</code> 方法,该方法被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">...</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</title>\\n \\t<meta name=\\"description\\" content=\\"RTMFP 是 Adobe 开发的基于 UDP 协议的实时传输媒体流协议,支持 P2P 传输,具有较高的实时性和安全性。它的主要应用场景是视频通信、语音通信和网络游戏。OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。Cumulus 是一个基于 OpenRTMFP 的服务器,提供 RTMFP 服务。它具有轻量级、跨平台和可扩展的特点,并且还提供了负载均衡和可扩展性解决方案。YY 语音的 Web 端音视频流媒体能力,正是基于 RTMFP 协议做的迭代优化实现的。本文是船长关于这个系列文章的第一篇。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-09T18:57:19+00:00\\" class=\\"by-line\\">09 Apr 2012, 广州 | 作者 麦克船长 | 总计 8401 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfp是什么\\" id=\\"markdown-toc-一rtmfp是什么\\">一、RTMFP 是什么?</a> <ul>\\n <li><a href=\\"#文件分享-p2p-和实时流媒体-p2p-的区别是什么\\" id=\\"markdown-toc-文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</a></li>\\n <li><a href=\\"#rtmfp-和-rtmp-之间的区别是什么\\" id=\\"markdown-toc-rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</a></li>\\n <li><a href=\\"#flash-player-支持-rtmfp-吗\\" id=\\"markdown-toc-flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</a></li>\\n <li><a href=\\"#cumulus-使用-adobe-的-cirrus-key-吗\\" id=\\"markdown-toc-cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</a></li>\\n <li><a href=\\"#这个开源项目合法吗\\" id=\\"markdown-toc-这个开源项目合法吗\\">这个开源项目合法吗?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二openrtmfp和cumulus\\" id=\\"markdown-toc-二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</a></li>\\n <li><a href=\\"#三入门介绍与部署cumulusserver\\" id=\\"markdown-toc-三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</a> <ul>\\n <li><a href=\\"#1背景介绍\\" id=\\"markdown-toc-1背景介绍\\">1、背景介绍</a></li>\\n <li><a href=\\"#2准备工作\\" id=\\"markdown-toc-2准备工作\\">2、准备工作</a></li>\\n <li><a href=\\"#3安装\\" id=\\"markdown-toc-3安装\\">3、安装</a> <ul>\\n <li><a href=\\"#31外部依赖的安装\\" id=\\"markdown-toc-31外部依赖的安装\\">3.1、外部依赖的安装</a></li>\\n <li><a href=\\"#32安装openrtmfpcumulus\\" id=\\"markdown-toc-32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#4配置\\" id=\\"markdown-toc-4配置\\">4、配置</a></li>\\n <li><a href=\\"#5启动\\" id=\\"markdown-toc-5启动\\">5、启动</a></li>\\n <li><a href=\\"#6基本使用\\" id=\\"markdown-toc-6基本使用\\">6、基本使用</a></li>\\n <li><a href=\\"#7扩展cumulusserverserverapplication\\" id=\\"markdown-toc-7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三用lua编写helloworld应用扩展cumulusserver\\" id=\\"markdown-toc-三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</a> <ul>\\n <li><a href=\\"#1server-side\\" id=\\"markdown-toc-1server-side\\">1、Server-side</a> <ul>\\n <li><a href=\\"#11serverconfiguration\\" id=\\"markdown-toc-11serverconfiguration\\">1.1、Server configuration</a></li>\\n <li><a href=\\"#12applicationfile\\" id=\\"markdown-toc-12applicationfile\\">1.2、Application file</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2client-side\\" id=\\"markdown-toc-2client-side\\">2、Client-side</a></li>\\n <li><a href=\\"#3运行结果\\" id=\\"markdown-toc-3运行结果\\">3、运行结果</a></li>\\n <li><a href=\\"#4远程测试一个免费的测试服务器\\" id=\\"markdown-toc-4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<h3 id=\\"一rtmfp是什么\\">一、RTMFP 是什么?</h3>\\n\\n<p>Real-Time Media Flow Protocol(RTMFP)是 Adobe 开发的一种基于 UDP 并支持 P2P 的实时传输媒体流。主要特点是:高传输效率(可以使用压缩和算法来优化流量从而提高传输效率)、高实时性(可以保证媒体流的实时性使得视频通信和其他实时通信更加流畅)、支持 P2P 传输(减少对服务器的依赖从而减少带宽和服务器资源消耗)、高安全性(加密媒体流从而保证其安全性)。</p>\\n\\n<p>RTMFP 的主要应用场景包括:视频通信(视频聊天和视频会议)、语音通信(语音聊天、电话)、网络游戏。不过 RTMFP 目前仅有 Adobe 开发的版本,所以它并不是个开源项目,而是个商业化服务。那么有没有开源版本呢?</p>\\n\\n<h4 id=\\"文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</h4>\\n\\n<p>RTMFP 是一个 P2P 系统,但它仅针对实时通信时直接用户到用户之间的通信而设计,不能用于多个对等方之间进行文件共享(使用分段下载)。Facebook 在其 Pipe 应用中使用此协议将大文件直接在两个用户之间传输。</p>\\n\\n<h4 id=\\"rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</h4>\\n\\n<p>RTMP 是实时消息协议,RTMFP 代表实时媒体流协议。RTMFP 基于用户数据报协议(UDP),而 RTMP 基于传输控制协议(TCP)。\\n与 RTMP 不同,RTMFP 还支持直接从一个 Adobe Flash Player 传输数据到另一个,而无需经过服务器。</p>\\n\\n<h4 id=\\"flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</h4>\\n\\n<p>RTMFP 是基于用户数据报协议(UDP)的,而 RTMP 是基于传输控制协议(TCP)的。与 RTMP 不同,RTMFP 还支持直接在两个 Adobe Flash Player 之间发送数据,而不经过服务器。Flash Player 10.0 仅允许一对一通信进行 P2P,但从 10.1 开始允许应用程序级别的多播。Flash Player 查找适当的分发路由(覆盖网络),并可以将其分发到通过 P2P 连接的组。</p>\\n\\n<h4 id=\\"cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</h4>\\n\\n<p>不!当然,这是Cumulus的主要目标:成为Cirrus GPL的替代品。唯一的限制是:你的CPU,内存和单台机器的端口数。</p>\\n\\n<h4 id=\\"这个开源项目合法吗\\">这个开源项目合法吗?</h4>\\n\\n<p>在美国,数字千年版权法(Digital Millennium Copyright Act)规定,逆向工程用于协议互操作性是合法的。你可以在 WikiPedia 上查看相关讨论:</p>\\n\\n<ul>\\n <li>http://en.wikipedia.org/wiki/Real_Time_Media_Flow_Protocol</li>\\n <li>http://en.wikipedia.org/wiki/Proprietary_protocol</li>\\n <li>http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act</li>\\n</ul>\\n\\n<p>当逆向工程的目的是协议互操作性时,有法律先例。在美国,数字千年版权法(Digital Millennium Copyright Act)为逆向工程软件以使其与其他软件互操作提供了安全保障。</p>\\n\\n<h3 id=\\"二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</h3>\\n\\n<p>OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。它包含了 RTMFP 协议的实现,以及一些额外的功能,如媒体流传输、P2P 通信、脚本引擎和数据存储。</p>\\n\\n<p>Cumulus 是一个基于 OpenRTMFP 的服务器,是一个完整的开源且跨平台的 RTMFP 服务器,可通过脚本进行扩展。CumulusServer 根据 GPL 许可在考虑以下 4 个概念的情况下开发:速度、轻量、跨平台和可扩展。尽管尚未发布版本,但只有在 CumulusServer 经过测试和批准后才会将代码推送到 github。实际上,主要稳定的功能有:</p>\\n\\n<ul>\\n <li>P2P rendez-vous service</li>\\n <li>live streaming</li>\\n <li>RPC、pull、push exchange,实际上客户端和服务器之间的所有 AMF 可能交换</li>\\n <li>脚本引擎,用于创建自己的应用服务器或扩展 Cumulus 的功能</li>\\n <li>可扩展性和负载均衡解决方案</li>\\n</ul>\\n\\n<p>下面的内容是本篇 blog 的重点,包括两部分:先是 OpenRTMFP 应用的核心 CumulusServer 的入门介绍与部署,然后用 Lua 编写 HelloWorld 应用扩展 CumulusServer,我们开始吧!</p>\\n\\n<h3 id=\\"三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</h3>\\n\\n<h4 id=\\"1背景介绍\\">1、背景介绍</h4>\\n\\n<p>OpenRTMFP 可以帮助你实现 Flash 的实时应用的高并发扩展,OpenRTMFP/Cumulus 是基于 GNU General Public License 的。</p>\\n\\n<p>POCO:POrtable COmponents,是一个强大的开源 C++ 库。其在 C++ 开发中的角色,相当于 Java Class Library、苹果的 Cocoa、.NET framework。</p>\\n\\n<h4 id=\\"2准备工作\\">2、准备工作</h4>\\n\\n<p>下载:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><strong>External Dependencies</strong></th>\\n <th><strong>Official Site</strong></th>\\n <th><strong>Windows</strong></th>\\n <th><strong>Linux/OSX</strong></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>OpenSSL</td>\\n <td><a href=\\"http://www.slproweb.com/products/Win32OpenSSL.html\\">Official Site</a></td>\\n <td><a href=\\"http://www.slproweb.com/download/Win32OpenSSL_Light-1_0_1.exe\\">Download</a></td>\\n <td><a href=\\"http://www.openssl.org/source/openssl-1.0.1.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>Lua</td>\\n <td><a href=\\"http://www.lua.org/\\">Official Site</a></td>\\n <td><a href=\\"http://luaforwindows.googlecode.com/files/LuaForWindows_v5.1.4-45.exe\\">Download</a></td>\\n <td><a href=\\"http://www.lua.org/ftp/lua-5.1.5.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>POCO</td>\\n <td><a href=\\"http://pocoproject.org/\\">Official Site</a></td>\\n <td><a href=\\"http://downloads.sourceforge.net/project/poco/sources/poco-1.4.3/poco-1.4.3p1.zip\\">Download</a></td>\\n <td><a href=\\"https://sourceforge.net/projects/poco/files/sources/poco-1.4.3/poco-1.4.3p1.tar.gz/download\\">Download</a></td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>注意:POCO for linux 版本必须是 1.4.0 或更高,否则会引起 TCP 相关的 bug。</p>\\n\\n<h4 id=\\"3安装\\">3、安装</h4>\\n\\n<h5 id=\\"31外部依赖的安装\\">3.1、外部依赖的安装</h5>\\n\\n<p>Windows 下略,Linux 下基本就是:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>./configuremakesudo\\n<span class=\\"nv\\">$ </span>make <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</h5>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>OpenRTMFP-Cumulus/CumulusLib\\n<span class=\\"nv\\">$ </span>make\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd</span> ../CumulusServer\\n<span class=\\"nv\\">$ </span>make\\n</code></pre></div></div>\\n\\n<p>如果出现了 <code class=\\"language-plaintext highlighter-rouge\\">.h</code> 文件、lib 库找不到的情况,请修改 OpenRTMFP-Cumulus/CumulusLib/Makefile 或 OpenRTMFP-Cumulus/CumulusServer/Makefile。</p>\\n\\n<h4 id=\\"4配置\\">4、配置</h4>\\n\\n<p>通过编写 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP-Cumulus/CumulusServer/CumulusServer.ini</code> 文件来为 OpenRTMFP-Cumulus 进行个性化配置(默认是没有这个文件的),这个文件的内容形如:</p>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1985</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span>\\n<span class=\\"n\\">name</span><span class=\\"o\\">=</span><span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span><span class=\\"o\\">=</span><span class=\\"n\\">C</span><span class=\\"p\\">:</span><span class=\\"o\\">/</span><span class=\\"n\\">CumulusServer</span><span class=\\"o\\">/</span><span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<p>一些字段的设置含义如下,摘自:<a href=\\"https://github.com/OpenRTMFP/Cumulus/wiki/Installation\\">地址</a>。</p>\\n\\n<ul>\\n <li>公开给 Client 的端口号 <code class=\\"language-plaintext highlighter-rouge\\">port</code>,默认值是 1935(RTMFP 服务器的默认端口),用于 CumulusServer 监听 RTMFP 请求。</li>\\n <li>UDP 缓冲区字节数 <code class=\\"language-plaintext highlighter-rouge\\">udpBufferSize</code>, allows to change the size in bytes of UDP reception and sending buffer. Increases this value if your operating system has a default value too lower for important loads.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAliveServer</code>, time in seconds for periodically sending packets keep-alive with server, 15s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAlivePeer</code>, time in seconds for periodically sending packets keep-alive between peers, 10s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.activated</code>, activate or not the edges server on the RTMFP server (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, CumulusServer stays a RTMFP server without edges ability (default value is false).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.port</code>, port for the edges server, to accept incoming new CumulusEdge instances (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, it’s the port 1936.</li>\\n</ul>\\n\\n<blockquote>\\n <p>Warning: This port will receive plain text request from edges, for this purpose it should not be made public. It’s very important for security consideration. It must be available only for CumulusEdge instances, and anything else.</p>\\n</blockquote>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.attemptsBeforeFallback</code>, number of CumulusEdge attempt connections before falling back to CumulusServer (see CumulusEdge, Scalability page for more details about CumulusEdge). By default the value is 2 (in practical, 2 attempts happens after 5 sec approximately).</li>\\n <li>SMTP IP 地址 <code class=\\"language-plaintext highlighter-rouge\\">smtp.host</code>, configure a SMTP host to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is localhost.</li>\\n <li>SMTP 端口 <code class=\\"language-plaintext highlighter-rouge\\">smtp.port</code>, configure a SMTP port to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 25.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">smtp.timeout</code>, configure a SMTP timeout session in seconds to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 60 seconds.</li>\\n <li>日志路径 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code>,默认是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/logsby</code>。</li>\\n <li>日志文件名称 <code class=\\"language-plaintext highlighter-rouge\\">logs.name</code>,默认是<code class=\\"language-plaintext highlighter-rouge\\">log</code>。</li>\\n</ul>\\n\\n<h4 id=\\"5启动\\">5、启动</h4>\\n\\n<p>Windows 下的启动方法为:</p>\\n\\n<pre><code class=\\"language-dos\\">$ CumulusServer.exe /registerService [/displayName=CumulusServer /description=\\"Open Source RTMFP Server\\" /startup=automatic]\\n</code></pre>\\n\\n<p>Unix-like 下的启动方法为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"o\\">[</span><span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>/var/run/CumulusServer.pid]\\n</code></pre></div></div>\\n\\n<p>具体地,我的启动命令为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>./CumulusServer.pid\\n</code></pre></div></div>\\n\\n<h4 id=\\"6基本使用\\">6、基本使用</h4>\\n\\n<p>本地 Flash client 可以通过如下语句连接:</p>\\n\\n<div class=\\"language-as highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">$</span> <span class=\\"kd\\">var</span> <span class=\\"nx\\">nc</span><span class=\\"o\\">:</span><span class=\\"nx\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nx\\">NetConnection</span><span class=\\"p\\">()</span><span class=\\"o\\">;</span><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost/\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>RTMFP默认是采用1935端口,如果你特别指定了其他端口,比如12345,请使用如下方式:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>nc.connect(\\"rtmfp://localhost:12345/\\");\\n</code></pre></div></div>\\n\\n<h4 id=\\"7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</h4>\\n\\n<p>启动CumulusServer后,会在可执行文件的目录下出现一个www目录,该目录的作用,就是作为 Server Application 的默认根目录。具体的对应关系如下:</p>\\n\\n<blockquote>\\n <p>rtmfp://host:port/ -&gt; [CumulusServer folder]/www/main.lua (root application)</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/myApplication -&gt; [CumulusServer folder]/www/myApplication/main.lua</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/Games/myGame -&gt; [CumulusServer folder]/www/Games/myGame/main.lua</p>\\n</blockquote>\\n\\n<p>另外要提醒的是,如果main.lua文件被修改,则不需要重启 CumulusServer,因为 Server Application 的创建是一种动态的方式。</p>\\n\\n<h3 id=\\"三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</h3>\\n\\n<p>下面的这个实例是在本地(Client 与 Server 位于同一机器上)测试的。</p>\\n\\n<h4 id=\\"1server-side\\">1、Server-side</h4>\\n\\n<h5 id=\\"11serverconfiguration\\">1.1、Server configuration</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span> <span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1935</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span><span class=\\"n\\">name</span> <span class=\\"o\\">=</span> <span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12applicationfile\\">1.2、Application file</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">function</span> <span class=\\"nf\\">onConnection</span><span class=\\"p\\">(</span><span class=\\"n\\">client</span><span class=\\"p\\">,</span><span class=\\"n\\">response</span><span class=\\"p\\">,</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">function</span> <span class=\\"nf\\">client</span><span class=\\"p\\">:</span><span class=\\"n\\">test</span><span class=\\"p\\">(</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">firstname</span> <span class=\\"o\\">=</span> <span class=\\"n\\">unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">arg</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"s2\\">\\"Hello \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">firstname</span><span class=\\"o\\">..</span><span class=\\"s2\\">\\" \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">name</span>\\n <span class=\\"k\\">end</span>\\n <span class=\\"k\\">end</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2client-side\\">2、Client-side</h4>\\n\\n<div class=\\"language-java highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\">// CumulusClient.as</span>\\n\\n<span class=\\"kn\\">package</span> <span class=\\"err\\">{</span>\\n <span class=\\"nn\\">import</span> <span class=\\"n\\">flash</span><span class=\\"o\\">.</span><span class=\\"na\\">display</span><span class=\\"o\\">.</span><span class=\\"na\\">Sprite</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetConnection</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetStream</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.Responder</span><span class=\\"o\\">;</span>\\n\\n <span class=\\"kd\\">public</span> <span class=\\"kd\\">class</span> <span class=\\"nc\\">CumulusClient</span> <span class=\\"kd\\">extends</span> <span class=\\"nc\\">Sprite</span> <span class=\\"o\\">{</span>\\n <span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">nc:</span><span class=\\"nc\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t<span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">ns:</span><span class=\\"nc\\">NetStream</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t\\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">CumulusClient</span><span class=\\"o\\">()</span> <span class=\\"o\\">{</span>\\n <span class=\\"n\\">nc</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nc\\">NetConnection</span><span class=\\"o\\">();</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">connect</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"rtmfp://localhost\\"</span><span class=\\"o\\">);</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">client</span> <span class=\\"o\\">=</span> <span class=\\"k\\">this</span><span class=\\"o\\">;</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">call</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"test\\"</span><span class=\\"o\\">,</span><span class=\\"k\\">new</span> <span class=\\"nc\\">Responder</span><span class=\\"o\\">(</span><span class=\\"n\\">onResult</span><span class=\\"o\\">,</span><span class=\\"n\\">onStatus</span><span class=\\"o\\">),</span> <span class=\\"s\\">\\"OpenRTMFP/Cumulus\\"</span><span class=\\"o\\">,</span> <span class=\\"s\\">\\"World\\"</span><span class=\\"o\\">)</span>\\n <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">close</span><span class=\\"o\\">():</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span> \\n\\t\\t\\t<span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">close</span><span class=\\"o\\">();</span>\\n \\t<span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onStatus</span><span class=\\"o\\">(</span><span class=\\"nl\\">status:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">status</span><span class=\\"o\\">.</span><span class=\\"na\\">description</span><span class=\\"o\\">)</span>\\n\\t <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onResult</span><span class=\\"o\\">(</span><span class=\\"nl\\">response:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">response</span><span class=\\"o\\">)</span> <span class=\\"c1\\">// expected to display \\"Hello World OpenRTMFP/Cumulus\\" </span>\\n\\t <span class=\\"o\\">}</span> \\n\\t<span class=\\"o\\">}</span>\\n<span class=\\"o\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3运行结果\\">3、运行结果</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Hello World OpenRTMFP/Cumulus\\n[SWF] CumulusClient.swf - 解压缩后为 1,776 个字节\\n[卸装 SWF] CumulusClient.swf\\n</code></pre></div></div>\\n\\n<h4 id=\\"4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</h4>\\n\\n<p>获取 Developer Key 的地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/index.jsp\\n</code></pre></div></div>\\n\\n<p>服务器配置信息:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Server: amd64 OS: Linux 2.6.18-028stab095.1\\nServer IP: 108.59.252.39\\nOpenRTMFP as of: 22.Feb.2012\\n</code></pre></div></div>\\n\\n<p>编写服务器段应用地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/manage_ssls.jsp\\n</code></pre></div></div>\\n\\n<p>快去试试吧 :)</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"思考":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>不要船开远了,就忘了为什么启航</title>\\n \\t<meta name=\\"description\\" content=\\"2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>不要船开远了,就忘了为什么启航</h2>\\t\\t\\n\\t<time datetime=\\"2022-08-11T15:53:57+00:00\\" class=\\"by-line\\">11 Aug 2022, 杭州 | 作者 麦克船长 | 总计 3223 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>偶然翻到 2020.06.11 刚来到阿里时写的一篇内容(我是 2020 年的 6 月 4 日我入职阿里巴巴集团),是有关于来阿里的期待、对这家公司的一些粗浅初步的理解。此时再翻来看看,最大的感触就是,提醒自己勿忘初心。</p>\\n\\n<p>在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:</p>\\n\\n<ul>\\n <li>很多人是带着梦想来阿里的,那么我的梦想是什么呢?</li>\\n <li>最喜欢新六脉的哪句话?为什么?</li>\\n <li>关于阿里企业价值观:为什么要接受这套价值观?</li>\\n <li>价值观的本质意义(极度务实视角)是什么?</li>\\n <li>Landing 的 SOP</li>\\n <li>问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2020 年平安夜 · 百年湖畔 87 期合影</p>\\n\\n<h3 id=\\"很多人是带着梦想来阿里的那么我的梦想是什么呢\\">很多人是带着梦想来阿里的,那么我的梦想是什么呢?</h3>\\n\\n<p>Christensen 在《创新者的窘境》中提到:每一次技术更迭,都需要破坏性创新,而破坏性创新在前一次技术更迭的胜出者内部是很难生长出来的。阿里诞生以来,不断地创造第二增长曲线:阿里巴巴、淘宝、支付宝、天猫、阿里云、钉钉 …… 这让我非常好奇。其中很多产品穿越多个时间周期,期间不断创造内生二次曲线。</p>\\n\\n<p>但是阿里也一样错失了很多,微信、美团、拼多多、抖/快…… 等等很多产品诞生在了其他公司,还有某些产品在不断的科技更迭中自身生长出了第二曲线。</p>\\n\\n<p>因此我来阿里的梦想也非常明确:<strong>参与或创造一次(甚至多次)第二曲线,可以是新产品,也可以是原有产品内生的。在这个过程中获得个人成长、个人价值。</strong></p>\\n\\n<p>一直以来,我有三个最想实现或得到的东西:LOVE、CREATION、FREEDOM。随着生活与工作的前行,对这三者的理解,在不断加深。在这个问题里,我想应该是讨论”CREATION”。</p>\\n\\n<p>CREATION 上,我的梦想的范式,大概是从自己中学时代就确立了,在某一次人类社会变革浪潮中,扮演有一定权重的角色。这里面有几个变量:<strong>什么领域(F)的变革;什么规模(S)的变革;多大的权重(W);什么角色(R)。</strong></p>\\n\\n<p>F 这个变量,我在中学及大学时代逐渐明确,是以相对普适的产品形式输出结果并对社会变革产生积极作用。后来越来越明确为科技与商业结合的领域。</p>\\n\\n<p>S、W 这两个变量,自然是越大越好。因此我会希望能够构建尽可能大的机会,或者参与到尽可能大的机会中。R 希望是有强烈 Ownership 的身份。</p>\\n\\n<p>因此过去几年我选择了创业。创业就像冲浪,你抓住一次浪并完成漂亮的动作,就是一次不算失败的创业。但是如果一个浪没抓住,你去追它是没意义的,而应该等待下一个浪。我认为在未来 5~10 年内难以出现规模能大到令我足够兴奋的科技浪潮。大浪潮中属于创业者的大机会很多,而中小浪潮的大机会基本只属于大平台,那么为了在壮年期做获得我的 CREATION,我选择了加入阿里这样的大平台。</p>\\n\\n<p>在最后做决定以及初来阿里的那个人生转折点,作为老阿里人的曲洋老师对我说的一句话,深深地鼓励了我,他说:”带着创业气质,把这里当你的舞台折腾!”</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2020 年双十一 · 淘宝 KO</p>\\n\\n<h3 id=\\"最喜欢新六脉的哪句话为什么\\">最喜欢新六脉的哪句话?为什么?</h3>\\n\\n<p>最喜欢的是“因为信任所以简单”。</p>\\n\\n<p>我一直认为人最重要的两个元特质是”真实”和“谦逊”,由”真实”可以塑造自我(对内)、构建信任(对外),后者可以带来清晰的边界,继而实现人与人之间高效的互动(这种互动包括各种人际关系在内,如婚姻、合伙、共事、合作等等)。</p>\\n\\n<p><strong>事物(虚实皆可)呈现在人的认知中,会得到三方面的投影:facts、opinion、feeling。如果我们足够真实,当我们需要把这三方面呈现给他人时,双方就能顺畅建立信任。信任的结果,就对应到这三方面:彼此之间建立共识(facts)、求同存异(opinion)、尊重感受(feeling),这就是”简单”。</strong></p>\\n\\n<p>另外一句是鼓励自己勇于绽放的一条:「此次此刻,非我莫属」。</p>\\n\\n<p>激情、自信、积极…… 通常行为统一表现为“勇于绽放自己”,绽放有表达(语言)与投身(行为)两种表现形式。更进一步推进就是”此次此刻,非我莫属”的阿里价值观。</p>\\n\\n<p>低调、稳重、谦逊,其实与“此次此刻,非我莫属“,并不矛盾。这点是我来到阿里后,发现自己在过去这些年的创业中已经不知不觉改变了,从 Introvert 逐渐变成了 Extrovert 的人,而且从曾经 social 中消耗能量,逐渐变为我现在可以感知到获得能量。这种变化,是我最近来阿里才确认发生的,此前因为自己创业者的身份没有察觉这种变化的发生。</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年秋 · 径山之行</p>\\n\\n<h3 id=\\"关于阿里企业价值观为什么要接受这套价值观\\">关于阿里企业价值观:为什么要接受这套价值观?</h3>\\n\\n<p>马老师和老逍都提到这个:我们是寻找同路人,而不是教育别人。这其实非常明晰地解释了为什么阿里要构建一个毛细血管网络一样的政委体系。基于这种用人理念,政委体系不敢说是最优解,但一定是优解(而且是否有更优解的论证没有意义)。</p>\\n\\n<p>对于个人,我的理解是要做两件事:<strong>1)构建自己的价值观体系(初始化);2)寻找价值观契合的公司(做匹配)。这两点里,没有任何地方提到”你要改变价值观为了契合你所在的公司”。</strong></p>\\n\\n<p>而企业价值观呢,其实可以分两部分看待:普世价值观、独特价值观。前者因为是普世的,所以到了哪个公司这种价值观都对,这点老逍也提了,比如“客户第一”。后者是个性化的,但不存在孰高孰低,就像一个人内向还是外向,你不能说哪个是错的。</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-4.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年双十一 · 天天特卖团队</p>\\n\\n<h3 id=\\"价值观的本质意义极度务实视角是什么\\">价值观的本质意义(极度务实视角)是什么?</h3>\\n\\n<p><strong>在充分考虑价值观适配使命、愿景基础上,价值观本身的意义,在和风细雨时(即企业价值观与其他价值判断相 match 时),是看不到的。但在暴风骤雨时(即企业价值观与其他价值判断相冲突时),就能显示其实实在在的作用了。</strong>我认为包括三类,前两个是阿里整体视角,第三个是阿里内部:</p>\\n\\n<ul>\\n <li>经济体内,阿里与其他生态位的冲突或损益关系,如曾经的美蘑口一役。</li>\\n <li>经济体内,其他的生态位之间的冲突或损益关系,如曾经的十月围城。</li>\\n <li>阿里人的行为价值判断,如最近的钉钉代考事件、过往的各类廉政事件。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-5.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年冬 · 淘宝天猫合并前合影</p>\\n\\n<h3 id=\\"landing的sop\\">Landing 的 SOP</h3>\\n\\n<p>大家都说 landing 充满挑战,马老师其实给出了 landing 的 SOP 三部曲:<strong>一起打过仗、一起创过新、一起度过难。三个经历都 close 才算 smooth landing。</strong></p>\\n\\n<p>集团人才策略层面、HR 实操层面、Leader 层面、,对于新人 landing 能做到什么程度的保障,其实每个新人感受到的不尽相同:</p>\\n\\n<ul>\\n <li>集团层面,始终是在构建更好的新人 landing 环境的,这符合自身价值,这能打下很好的底子。</li>\\n <li>实操层面,包括面试阶段对候选人的价值观判断、预期管理,面试及入职后公司文化及人才体系的事实呈现、内化吸收和长期解惑。</li>\\n <li>Leader 层面,这是新人体感最强烈的部分,也是最重要的部分。尽管拥抱变化,但首先 Leader 需要给出尽可能最全面的考虑,其次是对候选人的预期管理。好的 Leader 会给候选人提供合理的着陆点、多个降落伞、缓冲垫,完成 smooth landing。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-6.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年夏 · 出差厦漳泉</p>\\n\\n<h3 id=\\"问问自己来到阿里如果初期我可能需要做一点改变那会是什么\\">问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</h3>\\n\\n<p>曾经个人的激情与动力,常来自于“增长”。常说<strong>高增长掩盖一切</strong>,所以未来在阿里如果不能如创业般快速获得反馈得到积极结果,并且大平台中必然要接受大量关联方共同参与项目而导致的效率降低,因此我要逐渐改变自己,重新适应这种环境下的激情与动力获得方式。</p>\\n\\n<p>另一方面,作为创业公司的负责人,工作中鲜有因为内部原因而无法推进的事情,但是扮演肩部或腰部角色时,需要接受头部决策的一定程度不可控,这是我需要作出的适应与改变。关于这一点,我在几个月前就已经在做预期管理和心态调整,我认为以创业者的强适应性,这可能并不会是问题,但是我习惯于保持谨慎的乐观来面对自己。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</title>\\n \\t<meta name=\\"description\\" content=\\"阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</h2>\\t\\t\\n\\t<time datetime=\\"2021-11-11T19:59:43+00:00\\" class=\\"by-line\\">11 Nov 2021, 杭州 | 作者 麦克船长 | 总计 917 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2021-11-11-captain-tttm-1.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h3 id=\\"天天特卖团队理念\\">天天特卖团队理念</h3>\\n\\n<h4 id=\\"特卖合伙人\\">特卖合伙人</h4>\\n\\n<p>以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。特卖核心管理团队每 Q 会进行一次班子建设通晒。</p>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-9.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h4 id=\\"如何理解协作\\">如何理解协作?</h4>\\n\\n<p>从长时间线来看,我们是为了不断积累信用,像一张信用卡一样,不断获得别人愿意支持我们的更大额度。不要事情结果还可以,而我们却没有积累到信用。互联网本质也是现代工业。而现代工业,一是社会分工,二是社会协作。想取得现代工业项目的结果,就要有更大的人才包容度、环境包容度。工作的结果就是在妥协与博弈中取得的,这是和光同尘的本质,也是现代工业复杂系统拿到结果的本质。只有这样我们才能让越来越多的人追随我们一起 do something,这种追随不一定只有上下级才是,而是愿意并且相信和我们能到达更远的地方。这背后的信用,要我们一步一个脚印地去积累,对他人给予的信任要保持敬畏、如履薄冰、懂得感恩。对每一段阶段性或长或短结束的合作,都要表达感谢。</p>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-8.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h4 id=\\"如何看待同学的优势及短板\\">如何看待同学的优势及短板?</h4>\\n\\n<ul>\\n <li>优势:讲优势有两个可能的目的,要么组织会在未来任务分配上重点考虑发挥该同学优势的事情,要告诉 TA,要激励 TA,是 TA 前行的自信来源之一。要么是对于同学也把握不准的特点,我们明确告诉 TA 这是你被我欣赏的优点。</li>\\n <li>短板:什么是要讲的短板?未来一段时间,最期待你补足提升的。一旦这方面显著进步,就会向上迈进很大一步,甚至可以突破自己当下成长的瓶颈。要花多少篇幅讲?要比优势,有更大篇幅去讲。讲完就结束了么?对这个短板,一定要表达态度,也一定要对是否有方法、什么方法来补足短板要和同学沟通。</li>\\n <li>无论是优势,还是短板,要说到点儿上,不要说片儿汤话。要让同学们能够引起思考、启发的。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-10.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h3 id=\\"天天特卖期待你的加入\\">天天特卖期待你的加入!</h3>\\n\\n<p>新天天特卖缘起于「手淘下沉市场战役)」,于 2021 年初上线,以「极致性价比货源、裸价直降、全网比价、买贵必赔」打造手淘极致价格敏感人群的购物阵地。目前天天特卖团队有行业运营、用户运营、数据策略、整合营销、直播运营、内容运营等岗位,有兴趣的同学可以钉钉随时找我,期待你的加入!</p>\\n\\n<h4 id=\\"欢迎添加我的微信sinosuperman-推荐自荐--\\">欢迎添加我的微信:sinosuperman 推荐、自荐 ^ ^</h4>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-11.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-2.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-3.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-4.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-5.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-6.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-7.jpg\\" alt=\\"imagee\\" /></p>\\n\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的阿里一年香(入职阿里一周年)</title>\\n \\t<meta name=\\"description\\" content=\\"本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的阿里一年香(入职阿里一周年)</h2>\\t\\t\\n\\t<time datetime=\\"2021-06-04T15:42:43+00:00\\" class=\\"by-line\\">04 Jun 2021, 杭州 | 作者 麦克船长 | 总计 304 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>To 钟超</p>\\n\\n<p>1 周年快乐!很开心我们有这样一段共事的机会,虽开始时有些许波折,但随着进一步相处,我们很快能做到彼此欣赏、英雄相惜、默契配合,也特别感谢你对我的信任和支持,这是一切共事的基础。你强大的自驱力、脑力、对新事物的理解学习能力,都是最近几手新人里比较突出的。特别钦佩于你的执着和初性,对一件事认定后,迸发出的强大战斗力和决心。今天特卖这个新业务需要扎下根基,还真的需要一些舍我其谁的胆魄和更为犀利的突破,我也相信「新特卖」能成为你在阿里又一代表作,我希望我们的团队能为之骄傲和自豪,我们能不负公司所托,真正在下沉市场这场硬仗上有所建树,井取得令我们自己感到骄傲的突破,一起加油。</p>\\n\\n<p>From 麦克船长的主管</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>担任淘宝产品总负责人的双十一,是怎样的体验?</title>\\n \\t<meta name=\\"description\\" content=\\"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>担任淘宝产品总负责人的双十一,是怎样的体验?</h2>\\t\\t\\n\\t<time datetime=\\"2020-11-11T15:59:43+00:00\\" class=\\"by-line\\">11 Nov 2020, 杭州 | 作者 麦克船长 | 总计 138 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\\n\\n<p><img src=\\"/img/src/2020-11-11-captain-double-eleven-1.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-2.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-3.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-4.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-5.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-6.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-7.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-8.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-9.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-10.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-11.jpg\\" alt=\\"image\\" /></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>疫后怎么做餐饮品牌?三叉戟模式或成标配</title>\\n \\t<meta name=\\"description\\" content=\\"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>疫后怎么做餐饮品牌?三叉戟模式或成标配</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-14T16:42:43+00:00\\" class=\\"by-line\\">14 Apr 2020, 杭州 | 作者 麦克船长 | 总计 1845 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\\n\\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\\n\\n<ul>\\n <li>外卖</li>\\n <li>做饭</li>\\n <li>速食</li>\\n</ul>\\n\\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\\n\\n<h3 id=\\"标品化外卖业务\\">标品化外卖业务</h3>\\n\\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\\n\\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\\n\\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\\n\\n<ul>\\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\\n</ul>\\n\\n<h3 id=\\"社区生鲜前置仓\\">社区生鲜前置仓</h3>\\n\\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\\n\\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\\n\\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\\n\\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\\n\\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\\n\\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\\n</ul>\\n\\n<h3 id=\\"线上预包装食品\\">线上预包装食品</h3>\\n\\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\\n\\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\\n\\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\\n</ul>\\n\\n<h3 id=\\"线下重构新餐饮时代到来\\">线下重构,新餐饮时代到来</h3>\\n\\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\\n\\n<ul>\\n <li>用「标品化外卖」覆盖外卖场景</li>\\n <li>用「生鲜前置仓」覆盖做饭场景</li>\\n <li>用「预包装食品」覆盖速食场景</li>\\n</ul>\\n\\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\\n\\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\\n\\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>延迟满足,才有自由</title>\\n \\t<meta name=\\"description\\" content=\\"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>延迟满足,才有自由</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-11T06:18:03+00:00\\" class=\\"by-line\\">11 Apr 2020, 杭州 | 作者 麦克船长 | 总计 4478 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\\n\\n<h3 id=\\"1儿童时期的延迟满足\\">1、儿童时期的延迟满足</h3>\\n\\n<h4 id=\\"棉花糖实验\\">棉花糖实验</h4>\\n\\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\\n\\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\\n\\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\\n\\n<p>先讲两个我自己的例子吧。</p>\\n\\n<h4 id=\\"黑加仑零食\\">黑加仑零食</h4>\\n\\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\\n\\n<h4 id=\\"暑假作业\\">暑假作业</h4>\\n\\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\\n\\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\\n\\n<h4 id=\\"延迟满足的背后是意志力自控力\\">延迟满足的背后,是意志力/自控力</h4>\\n\\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\\n\\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\\n\\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\\n\\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\\n\\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\\n\\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\\n\\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\\n\\n<h3 id=\\"2快乐理论挑战延迟满足\\">2、快乐理论,挑战延迟满足</h3>\\n\\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\\n\\n<h4 id=\\"烂苹果\\">烂苹果</h4>\\n\\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\\n\\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\\n\\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\\n\\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\\n\\n<h4 id=\\"快乐理论\\">快乐理论</h4>\\n\\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\\n\\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\\n\\n<p>但这个理论角度对吗?</p>\\n\\n<h3 id=\\"3我们应该在哪个范畴内讨论延迟满足\\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\\n\\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\\n\\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\\n\\n<h4 id=\\"仅有正反馈刺激的奶头乐\\">仅有正反馈刺激的奶头乐</h4>\\n\\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\\n\\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\\n\\n<h4 id=\\"延迟满足重在顺序而非时间\\">延迟满足重在顺序,而非时间</h4>\\n\\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\\n\\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\\n\\n<h3 id=\\"4常见的延迟满足与即时满足\\">4、常见的延迟满足与即时满足</h3>\\n\\n<h4 id=\\"初阶对手vs浅度延迟满足\\">初阶对手 vs 浅度延迟满足</h4>\\n\\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\\n\\n<ul>\\n <li>\\n <p>睡懒觉</p>\\n </li>\\n <li>\\n <p>过量饮食</p>\\n </li>\\n <li>\\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\\n </li>\\n <li>\\n <p>冲动情绪</p>\\n </li>\\n <li>\\n <p>炫耀(粗俗说就是装B)</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\\n\\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\\n\\n<h4 id=\\"中阶对手vs中度延迟满足\\">中阶对手 vs 中度延迟满足</h4>\\n\\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\\n\\n<ul>\\n <li>\\n <p>在家边看电视/视频,边学习/工作</p>\\n </li>\\n <li>\\n <p>依赖二手知识,而怠于一手知识</p>\\n </li>\\n <li>\\n <p>刷知乎、行业资讯 APP</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>例子会有很多,我们仅稍微说下这三个。</p>\\n\\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\\n\\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\\n\\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\\n\\n<h4 id=\\"高阶对手vs深度延迟满足\\">高阶对手 vs 深度延迟满足</h4>\\n\\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\\n\\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\\n\\n<p>我这里就说一个影响很大的:</p>\\n\\n<p>* 急于行动</p>\\n\\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\\n\\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\\n\\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\\n\\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\\n\\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\\n\\n<h3 id=\\"5延迟满足的驻留时长\\">5、延迟满足的驻留时长</h3>\\n\\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\\n\\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\\n\\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\\n\\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\\n\\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\\n\\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\\n\\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\\n\\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>未来人工智能就是要:让普通人过上现在富豪们的生活</title>\\n \\t<meta name=\\"description\\" content=\\"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>未来人工智能就是要:让普通人过上现在富豪们的生活</h2>\\t\\t\\n\\t<time datetime=\\"2017-02-23T18:23:33+00:00\\" class=\\"by-line\\">23 Feb 2017, 北京 | 作者 麦克船长 | 总计 627 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\\n\\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\\n\\n<h4 id=\\"但是我没有钱呢\\">但是我没有钱呢?</h4>\\n\\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\\n\\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\\n\\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\\n\\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\\n\\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\\n\\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\\n\\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>我们是应该「断舍离」还是「念念不忘,必有回响」</title>\\n \\t<meta name=\\"description\\" content=\\"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>我们是应该「断舍离」还是「念念不忘,必有回响」</h2>\\t\\t\\n\\t<time datetime=\\"2017-01-31T20:59:21+00:00\\" class=\\"by-line\\">31 Jan 2017, 北京 | 作者 麦克船长 | 总计 2577 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"引子\\">引子</h3>\\n\\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\\n\\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\\n\\n<p>「嗨,我说老朱,讲讲你啊!」</p>\\n\\n<p>「我就不提啦。」</p>\\n\\n<p>「为什么啊?说说,说说!」</p>\\n\\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\\n\\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\\n\\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\\n\\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\\n\\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\\n\\n<blockquote>\\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\\n</blockquote>\\n\\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\\n\\n<h3 id=\\"断舍离\\">断舍离</h3>\\n\\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\\" alt=\\"image\\" /></p>\\n\\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\\n\\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\\n\\n<h3 id=\\"念念不忘必有回响\\">念念不忘,必有回响</h3>\\n\\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\\" alt=\\"image\\" /></p>\\n\\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\\n\\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\\n\\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\\n\\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\\n\\n<h3 id=\\"用正负反馈来判断何时何为\\">用「正负反馈」来判断何时何为?</h3>\\n\\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\\n\\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\\n\\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\\n\\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\\n\\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\\n\\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\\n\\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\\n\\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\\n\\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\\n\\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\\n\\n<p>然后,我们一起等待,那声回响。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>一名出色软件工程师的技术基本功:编程与工具</title>\\n \\t<meta name=\\"description\\" content=\\"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>一名出色软件工程师的技术基本功:编程与工具</h2>\\t\\t\\n\\t<time datetime=\\"2012-05-15T17:06:59+00:00\\" class=\\"by-line\\">15 May 2012, 广州 | 作者 麦克船长 | 总计 2132 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"0写在前面\\">0、写在前面</h3>\\n\\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\\n\\n<h3 id=\\"1编程\\">1、编程</h3>\\n\\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\\n\\n<h4 id=\\"如果你精通c语言\\">如果你精通 C 语言</h4>\\n\\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\\n\\n<h4 id=\\"如果你精通c语言-1\\">如果你精通 C++ 语言</h4>\\n\\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\\n\\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\\n\\n<h4 id=\\"精通的关键还是针对语言核心来说的\\">精通的关键,还是针对语言核心来说的。</h4>\\n\\n<p>第一,你要对这个语言的语法特性熟稔;</p>\\n\\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\\n\\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\\n\\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\\n\\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\\n\\n<h4 id=\\"与计算机网络的基础结构相关联的技术实现\\">与计算机、网络的基础结构相关联的技术实现</h4>\\n\\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\\n\\n<h3 id=\\"2工具\\">2、工具</h3>\\n\\n<p>对于工具有三个层面:</p>\\n\\n<p>第一,是熟练的使用一些工具。</p>\\n\\n<p>第二,是能够发现提高生产力的工具。</p>\\n\\n<p>第三,是能够在无可用工具时自己编写工具。</p>\\n\\n<p>那么都有哪些最最最基本的工具呢?</p>\\n\\n<h4 id=\\"ideintegrateddevelopmentenvironment\\">IDE(Integrated Development Environment)</h4>\\n\\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\\n\\n<h4 id=\\"vcsversioncontrolsystem\\">VCS(Version Control System)</h4>\\n\\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\\n\\n<blockquote>\\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black &amp; white.</p>\\n</blockquote>\\n\\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\\n\\n<h4 id=\\"clicommandlineinterface\\">CLI(Command Line Interface)</h4>\\n\\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\\n\\n<h3 id=\\"3结语\\">3、结语</h3>\\n\\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"Jekyll":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的 Jekyll 快速教程</title>\\n \\t<meta name=\\"description\\" content=\\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的 Jekyll 快速教程</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-23T19:43:02+00:00\\" class=\\"by-line\\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\\n\\n<h3 id=\\"part-1基本特点\\">Part 1、基本特点</h3>\\n\\n<h4 id=\\"一基本语法\\">一、基本语法</h4>\\n\\n<ul>\\n <li>变量:用双大括号表示变量 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code></li>\\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\\n <li>支持循环与分支结构:比如 <code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">if-elsif-else-endif</code> :可以使用 <code class=\\"language-plaintext highlighter-rouge\\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\\n</ul>\\n\\n<h4 id=\\"二典型-jekyll-项目结构及重要文件介绍\\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\\n\\n<h5 id=\\"1配置文件-_configyml\\">1、配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code></h5>\\n\\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\\"language-plaintext highlighter-rouge\\">encoding: utf-8</code> 这一条。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Site settings</span>\\n<span class=\\"na\\">encoding</span><span class=\\"pi\\">:</span> <span class=\\"s\\">utf-8</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">麦克船长的技术、产品与商业博客</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\\"</span>\\n<span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptain.com\\"</span>\\n<span class=\\"na\\">author</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">name</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Your</span><span class=\\"nv\\"> </span><span class=\\"s\\">Name\\"</span>\\n <span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptian.com\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Markdown and highlighter</span>\\n<span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Plugins</span>\\n<span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-paginate</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-sitemap</span>\\n</code></pre></div></div>\\n\\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Build settings</span>\\n<span class=\\"na\\">baseurl</span><span class=\\"pi\\">:</span> <span class=\\"c1\\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\\n<span class=\\"na\\">source</span><span class=\\"pi\\">:</span> <span class=\\"s\\">.</span>\\n<span class=\\"na\\">destination</span><span class=\\"pi\\">:</span> <span class=\\"s\\">./_site</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:title</span>\\n<span class=\\"na\\">paginate</span><span class=\\"pi\\">:</span> <span class=\\"m\\">20</span>\\n<span class=\\"na\\">paginate_path</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/page:num/</span>\\n<span class=\\"na\\">collections</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">posts</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">output</span><span class=\\"pi\\">:</span> <span class=\\"no\\">true</span>\\n <span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:year/:month/:day/:title/</span>\\n <span class=\\"na\\">directory</span><span class=\\"pi\\">:</span> <span class=\\"s\\">_posts</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2布局文件_layouts-目录下的文件规则\\">2、布局文件:<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的文件规则</h5>\\n\\n<p>Jekyll 的 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\\"language-plaintext highlighter-rouge\\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\\"language-plaintext highlighter-rouge\\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\\n\\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\\n\\n<h5 id=\\"3页面文件及其头部\\">3、页面文件及其头部</h5>\\n\\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">page</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/categories/</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">Categories</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>每个页面文件的头部都会有layout,并与 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的某个文件对应。</p>\\n\\n<h3 id=\\"part-2jekyll-中的全局变量\\">Part 2、Jekyll 中的全局变量</h3>\\n\\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:包含当前页面或文章的内容。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:包含有关网站的所有标签的信息。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\\n</ul>\\n\\n<p>这些变量可以在模板中使用,比如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;h1&gt;</span>{{ page.title }}<span class=\\"nt\\">&lt;/h1&gt;</span>\\n<span class=\\"nt\\">&lt;p&gt;</span>{{ site.description }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n<span class=\\"nt\\">&lt;ul&gt;</span>\\n {{ for category in site.categories %}\\n <span class=\\"nt\\">&lt;li&gt;</span>{{ category }}<span class=\\"nt\\">&lt;/li&gt;</span>\\n {{ endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span> \\n</code></pre></div></div>\\n\\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">my_custom_variable</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Hello</span><span class=\\"nv\\"> </span><span class=\\"s\\">World\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后就可以在模板文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\\n\\n<h4 id=\\"一site变量\\">一、site变量</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\\n\\n<h5 id=\\"1sitecategories\\">1、<code class=\\"language-plaintext highlighter-rouge\\">site.categories</code></h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\\"language-plaintext highlighter-rouge\\">first</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">category</code> 的名字,如下使用:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2sitepages\\">2、site.pages</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3其他常用属性\\">3、其他常用属性</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.title</code>:是网站的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.related_posts</code>:相关文章的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.data</code>:从 <code class=\\"language-plaintext highlighter-rouge\\">_data</code> 目录加载的数据。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.static_files</code>:静态文件的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.collections</code>:自定义集合的列表。</li>\\n</ul>\\n\\n<h4 id=\\"二page-变量\\">二、<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量</h4>\\n\\n<p>在 Jekyll 中,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量属性包括:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">layout</code>:表示页面使用的布局模板的名称。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">title</code>:表示页面的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">date</code>:表示页面的发布日期。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">categories</code>:表示页面所属的分类列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:表示页面所属的标签列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\\n</ul>\\n\\n<p>在模板中,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.属性名 }}</code> 的方式来访问 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.title }}</code>。此外,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量还有其他属性,如 <code class=\\"language-plaintext highlighter-rouge\\">permalink</code>、<code class=\\"language-plaintext highlighter-rouge\\">excerpt</code>、<code class=\\"language-plaintext highlighter-rouge\\">url</code> 等,可以根据需要调用。</p>\\n\\n<h3 id=\\"part-3控制结构\\">Part 3、控制结构</h3>\\n\\n<h4 id=\\"1if-else-分支结构\\">1、<code class=\\"language-plaintext highlighter-rouge\\">if-else</code> 分支结构</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ if tmp_var == \\"type1\\" %}\\n{{ elsif tmp_var == \\"type2\\" %}\\n{{ elsif tmp_var == \\"type3\\" %}\\n{{ elsif tmp_var == \\"type4\\" %}\\n{{ else tmp_var == \\"type5\\" %}\\n{{ endif %}\\n</code></pre></div></div>\\n\\n<h4 id=\\"2for-endfor-循环结构\\">2、<code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 循环结构</h4>\\n\\n<p>不带条件判断的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 循环如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for post in paginator.posts %}\\n\\t<span class=\\"c\\">&lt;!-- Your other sentences --&gt;</span>\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<p>带条件循环的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages | where: \\"dir\\", \\"categories\\" %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3jekyll-支持的其他结构包括\\">3、Jekyll 支持的其他结构包括:</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">capture</code> 用于捕获输出的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">cycle</code> 用于循环一组字符串的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">include</code> 用于包含其他文件的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">while</code> 用于在满足指定条件时执行代码的结构。</li>\\n</ul>\\n\\n<h3 id=\\"参考\\">参考:</h3>\\n\\n<p>1、<a href=\\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\\n2、<a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\\n \\t<meta name=\\"description\\" content=\\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-21T15:53:57+00:00\\" class=\\"by-line\\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#写在前面\\" id=\\"markdown-toc-写在前面\\">写在前面</a></li>\\n <li><a href=\\"#1github上的准备\\" id=\\"markdown-toc-1github上的准备\\">1、GitHub 上的准备</a></li>\\n <li><a href=\\"#2了解ruby和jekyll\\" id=\\"markdown-toc-2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</a></li>\\n <li><a href=\\"#3了解gem\\" id=\\"markdown-toc-3了解gem\\">3、了解 Gem</a></li>\\n <li><a href=\\"#4安装homebrew\\" id=\\"markdown-toc-4安装homebrew\\">4、安装 Homebrew</a></li>\\n <li><a href=\\"#5用homebrew安装ruby\\" id=\\"markdown-toc-5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</a></li>\\n <li><a href=\\"#6安装jekyll和bundler\\" id=\\"markdown-toc-6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</a></li>\\n <li><a href=\\"#7使用bundle管理包依赖关系\\" id=\\"markdown-toc-7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</a></li>\\n <li><a href=\\"#8本地启动一下看看\\" id=\\"markdown-toc-8本地启动一下看看\\">8、本地启动一下看看</a></li>\\n <li><a href=\\"#9用jekyll创建一个项目\\" id=\\"markdown-toc-9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</a></li>\\n <li><a href=\\"#10修改gemfile文件\\" id=\\"markdown-toc-10修改gemfile文件\\">10、修改 Gemfile 文件</a></li>\\n <li><a href=\\"#11配置githubpages\\" id=\\"markdown-toc-11配置githubpages\\">11、配置 Github Pages</a></li>\\n <li><a href=\\"#12配置一个jekylltheme\\" id=\\"markdown-toc-12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</a></li>\\n <li><a href=\\"#13设置自定义域名\\" id=\\"markdown-toc-13设置自定义域名\\">13、设置自定义域名</a></li>\\n <li><a href=\\"#14用-rouge-实现代码高亮\\" id=\\"markdown-toc-14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</a></li>\\n <li><a href=\\"#15一些扩展问题\\" id=\\"markdown-toc-15一些扩展问题\\">15、一些扩展问题</a> <ul>\\n <li><a href=\\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\" id=\\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\\n <li><a href=\\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\" id=\\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\\n <li><a href=\\"#q3如何为每篇文章添加一个目录\\" id=\\"markdown-toc-q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</a></li>\\n <li><a href=\\"#q4如何在-jekyll-中支持-katex\\" id=\\"markdown-toc-q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\\n <li><a href=\\"#在-githubio-上\\" id=\\"markdown-toc-在-githubio-上\\">在 GitHub.io 上</a></li>\\n <li><a href=\\"#如果不在-githubio-上则还需要额外工作\\" id=\\"markdown-toc-如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\\n <li><a href=\\"#使用示例\\" id=\\"markdown-toc-使用示例\\">使用示例</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#q5jekyll-中如何支持-graphviz-\\" id=\\"markdown-toc-q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\\n <li><a href=\\"#q6如何显示--或者--\\" id=\\"markdown-toc-q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#参考\\" id=\\"markdown-toc-参考\\">参考</a></li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\\n\\n<ul>\\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\\n</ul>\\n\\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\\n\\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\\n\\n<h3 id=\\"1github上的准备\\">1、GitHub 上的准备</h3>\\n\\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git clone https://github.com/username/username.github.io\\n</code></pre></div></div>\\n\\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git add <span class=\\"nt\\">--all</span>\\n<span class=\\"nv\\">$ </span>git commit <span class=\\"nt\\">-m</span> <span class=\\"s2\\">\\"Initial commit\\"</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</h3>\\n\\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\\n\\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\\n\\n<h3 id=\\"3了解gem\\">3、了解 Gem</h3>\\n\\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\\n\\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\\ngem sources -l\\n</code></pre></div></div>\\n\\n<h3 id=\\"4安装homebrew\\">4、安装 Homebrew</h3>\\n\\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>/bin/bash <span class=\\"nt\\">-c</span> <span class=\\"s2\\">\\"</span><span class=\\"si\\">$(</span>curl <span class=\\"nt\\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\\"si\\">)</span><span class=\\"s2\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\\n\\n<h3 id=\\"5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</h3>\\n\\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>brew <span class=\\"nb\\">install </span>chruby ruby-install xz\\n</code></pre></div></div>\\n\\n<p>安装 Ruby 的最新版本:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby-install ruby\\n</code></pre></div></div>\\n\\n<p>这时候提示如下问题:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"o\\">&gt;&gt;&gt;</span> Updating ruby versions ...\\n<span class=\\"o\\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\\"se\\">\\\\</span>\\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\\n<span class=\\"o\\">!!!</span> Failed to download ruby versions!\\n</code></pre></div></div>\\n\\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ echo \\"185.199.111.133 raw.githubusercontent.com\\" &gt;&gt; /etc/hosts\\n</code></pre></div></div>\\n\\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/chruby.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/auto.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"chruby ruby-3.1.2\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc <span class=\\"c\\"># run 'chruby' to see actual version</span>\\n</code></pre></div></div>\\n\\n<p>再看下 Ruby 版本对不对:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby <span class=\\"nt\\">-v</span>\\n</code></pre></div></div>\\n\\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\\n\\n<h3 id=\\"6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"nb\\">install </span>jekyll bundler\\n</code></pre></div></div>\\n\\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\\n\\n<h3 id=\\"7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</h3>\\n\\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">source</span> <span class=\\"s1\\">'https://rubygems.org'</span>\\ngem <span class=\\"s1\\">'nokogiri'</span>\\ngem <span class=\\"s1\\">'rack'</span>, <span class=\\"s1\\">'~&gt; 2.2.4'</span>\\ngem <span class=\\"s1\\">'rspec'</span>\\ngem <span class=\\"s1\\">'jekyll'</span>\\n</code></pre></div></div>\\n\\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ git add Gemfile Gemfile.lock\\n</code></pre></div></div>\\n\\n<h3 id=\\"8本地启动一下看看\\">8、本地启动一下看看</h3>\\n\\n<p>先用 bundle 如下命令来启动:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: none\\n Source: /Users/captain/Workspace/poechant.github.io\\n Destination: /Users/captain/Workspace/poechant.github.io/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n done in 0.014 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\\n Server address: http://127.0.0.1:4000\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git pull <span class=\\"nt\\">--no-rebase</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll new CaptainMikeBlog\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>CaptainMikeBlog\\n<span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n Jekyll Feed: Generating feed for posts\\n done in 0.365 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\\n Server address: http://127.0.0.1:4000/\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"10修改gemfile文件\\">10、修改 Gemfile 文件</h3>\\n\\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"s2\\">\\"github-pages\\"</span>, <span class=\\"s2\\">\\"~&gt; GITHUB-PAGES-VERSION\\"</span>, group: :jekyll_plugins\\n</code></pre></div></div>\\n\\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ bundle install\\n</code></pre></div></div>\\n\\n<p>再本地启动服务器测试:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>得到如下提示:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\\nPrepending <span class=\\"sb\\">`</span>bundle <span class=\\"nb\\">exec</span><span class=\\"sb\\">`</span> to your <span class=\\"nb\\">command </span>may solve this. <span class=\\"o\\">(</span>Gem::LoadError<span class=\\"o\\">)</span>\\n</code></pre></div></div>\\n\\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle add webrick\\n<span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\\n\\n<h3 id=\\"11配置githubpages\\">11、配置 Github Pages</h3>\\n\\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>baseurl: \\"\\"\\nurl: \\"http://your-username.github.io\\"\\n</code></pre></div></div>\\n\\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\\n\\n<h3 id=\\"12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</h3>\\n\\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_includes\\n_layouts\\n_sass\\ncss\\njs\\nimg\\n404.markdown\\nindex.html\\n</code></pre></div></div>\\n\\n<p>不同主题会有所不同,这里只列个大概。</p>\\n\\n<h3 id=\\"13设置自定义域名\\">13、设置自定义域名</h3>\\n\\n<p>添加四条 A 记录,记录值如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>185.199.108.153\\n185.199.109.153\\n185.199.110.153\\n185.199.111.153\\n</code></pre></div></div>\\n\\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\\n\\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\\n\\n<h3 id=\\"14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</h3>\\n\\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem install kramdom rouge\\n</code></pre></div></div>\\n\\n<p>然后配置 _config.yml 文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>然后用 rouge 创建 syntax.css 文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>rougify style github <span class=\\"o\\">&gt;</span> css/syntax.css\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">_include/head.html</code> 文件中添加:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/css/syntax.css\\"</span> <span class=\\"nt\\">/&gt;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"15一些扩展问题\\">15、一些扩展问题</h3>\\n\\n<h4 id=\\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\\n\\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是一篇文章</span>\\n<span class=\\"na\\">excerpt</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是文章的摘要</span>\\n<span class=\\"nn\\">---</span>\\n\\n这是文章的正文内容\\n</code></pre></div></div>\\n\\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;ul&gt;</span>\\n {% for post in paginator.posts %}\\n <span class=\\"nt\\">&lt;li&gt;</span>\\n <span class=\\"nt\\">&lt;h2&gt;&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{{ post.url }}\\"</span><span class=\\"nt\\">&gt;</span>{{ post.title }}<span class=\\"nt\\">&lt;/a&gt;&lt;/h2&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>{{ post.excerpt }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n <span class=\\"nt\\">&lt;/li&gt;</span>\\n {% endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\\n\\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\\n\\n<h4 id=\\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\\n\\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code>目录下创建一个<code class=\\"language-plaintext highlighter-rouge\\">category.html</code>文件:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>---\\nlayout: default\\n---\\n\\n<span class=\\"nt\\">&lt;div</span> <span class=\\"na\\">class=</span><span class=\\"s\\">\\"container\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n {% if site.categories[page.category] %}\\n {% for post in site.categories[page.category] %}\\n <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{% if site.baseurl == \\"</span><span class=\\"err\\">/\\"</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">else</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">|</span> <span class=\\"na\\">prepend:</span> <span class=\\"na\\">site.baseurl</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">endif</span> <span class=\\"err\\">%}\\"</span><span class=\\"nt\\">&gt;</span>\\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\\n <span class=\\"nt\\">&lt;/a&gt;</span>\\n {% endfor %}\\n {% else %}\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>No posts for this category. If you have something in mind, check <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/write\\"</span><span class=\\"nt\\">&gt;</span>Write For Us<span class=\\"nt\\">&lt;/a&gt;</span>page.<span class=\\"nt\\">&lt;/p&gt;</span>\\n {% endif %}\\n<span class=\\"nt\\">&lt;/div&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">include</span><span class=\\"pi\\">:</span> <span class=\\"pi\\">[</span><span class=\\"s1\\">'</span><span class=\\"s\\">_categories'</span><span class=\\"pi\\">]</span>\\n</code></pre></div></div>\\n\\n<p>在根目录创建一个<code class=\\"language-plaintext highlighter-rouge\\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\\"language-plaintext highlighter-rouge\\">ai.html</code>如下:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">category</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">人工智能</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s\\">This is the description.</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/category/ai</span>\\n<span class=\\"na\\">category</span><span class=\\"pi\\">:</span> <span class=\\"s\\">ai</span>\\n<span class=\\"na\\">category_type</span><span class=\\"pi\\">:</span> <span class=\\"s\\">tech</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>那么之后每次创建文件时,在头部写<code class=\\"language-plaintext highlighter-rouge\\">category</code>一定要与这些<code class=\\"language-plaintext highlighter-rouge\\">categories</code>中的<code class=\\"language-plaintext highlighter-rouge\\">html</code>文件对应起来。</p>\\n\\n<h4 id=\\"q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</h4>\\n\\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">*</span> TOC\\n{:toc}\\n</code></pre></div></div>\\n\\n<h4 id=\\"q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\\n\\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\\n\\n<h5 id=\\"在-githubio-上\\">在 GitHub.io 上</h5>\\n\\n<p>先修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">math_engine</span><span class=\\"pi\\">:</span> <span class=\\"s\\">katex</span>\\n</code></pre></div></div>\\n\\n<p>然后修改 <code class=\\"language-plaintext highlighter-rouge\\">_includes/head.html</code> 文件,在 <code class=\\"language-plaintext highlighter-rouge\\">&lt;head&gt;</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">&lt;/head&gt;</code> 中间:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\">&lt;!--KaTeX--&gt;</span>\\n <span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span>\\n <span class=\\"na\\">href=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script&gt;</span>\\n <span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">addEventListener</span><span class=\\"p\\">(</span><span class=\\"dl\\">\\"</span><span class=\\"s2\\">DOMContentLoaded</span><span class=\\"dl\\">\\"</span><span class=\\"p\\">,</span> <span class=\\"kd\\">function</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"nx\\">renderMathInElement</span><span class=\\"p\\">(</span><span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">body</span><span class=\\"p\\">,</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// ...options...</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"nt\\">&lt;/script&gt;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</h5>\\n\\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem <span class=\\"nb\\">install </span>kramdom-math-katex\\n\\ngem <span class=\\"nb\\">install </span>katex\\ngem <span class=\\"nb\\">install </span>execjs\\n\\ngem <span class=\\"nb\\">install </span>therubyracer\\ngem <span class=\\"nb\\">install </span>therubyrhino\\ngem <span class=\\"nb\\">install </span>duktape\\n</code></pre></div></div>\\n\\n<h5 id=\\"使用示例\\">使用示例</h5>\\n\\n<p>以如下方式输入输入如下内容:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}\\n$$ \\\\sum_{i=1}^{n} a_i $$\\n{% endraw %}\\n</code></pre></div></div>\\n\\n<p>就会得到一个数学公式:</p>\\n\\n\\\\[\\\\sum_{i=1}^{n} a_i\\\\]\\n\\n<h4 id=\\"q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\\n\\n<p>这要依赖 <code class=\\"language-plaintext highlighter-rouge\\">jekyll-graphviz-dot</code>,修改 <code class=\\"language-plaintext highlighter-rouge\\">Gemfile</code> 增加一句:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>group :jekyll_plugins <span class=\\"k\\">do\\n </span>gem <span class=\\"s2\\">\\"jekyll-graphviz-dot\\"</span>\\nend\\n</code></pre></div></div>\\n\\n<p>再修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 配置文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-graphviz</span>\\n</code></pre></div></div>\\n\\n<p>再在本地安装 graphviz,可以通过 <code class=\\"language-plaintext highlighter-rouge\\">conda install graphviz</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">brew install graphviz</code>。然后 <code class=\\"language-plaintext highlighter-rouge\\">bundle install</code> 再 <code class=\\"language-plaintext highlighter-rouge\\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\\n\\n<pre><code class=\\"language-graphviz\\">{% graph some graph title %}\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n{% endgraph %}\\n</code></pre>\\n\\n<p>如果看到如下效果,就说明你都配置成功了:</p>\\n\\n<div class=\\"graphviz-wrapper\\">\\n\\n<!-- Generated by graphviz version 2.43.0 (0)\\n -->\\n<!-- Title: G Pages: 1 -->\\n<svg role=\\"img\\" aria-label=\\"some graph title\\" width=\\"89pt\\" height=\\"188pt\\" viewBox=\\"0.00 0.00 89.00 188.00\\">\\n<title>some graph title</title>\\n<desc>\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n</desc>\\n\\n<g id=\\"graph0\\" class=\\"graph\\" transform=\\"scale(1 1) rotate(0) translate(4 184)\\">\\n<title>G</title>\\n<polygon fill=\\"white\\" stroke=\\"transparent\\" points=\\"-4,4 -4,-184 85,-184 85,4 -4,4\\" />\\n<!-- a -->\\n<g id=\\"node1\\" class=\\"node\\">\\n<title>a</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-162\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-158.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">a</text>\\n</g>\\n<!-- b -->\\n<g id=\\"node2\\" class=\\"node\\">\\n<title>b</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"27\\" cy=\\"-90\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"27\\" y=\\"-86.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">b</text>\\n</g>\\n<!-- a&#45;&gt;b -->\\n<g id=\\"edge1\\" class=\\"edge\\">\\n<title>a&#45;&gt;b</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\\" />\\n</g>\\n<!-- c -->\\n<g id=\\"node3\\" class=\\"node\\">\\n<title>c</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-18\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-14.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">c</text>\\n</g>\\n<!-- b&#45;&gt;c -->\\n<g id=\\"edge2\\" class=\\"edge\\">\\n<title>b&#45;&gt;c</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\\" />\\n</g>\\n<!-- c&#45;&gt;a -->\\n<g id=\\"edge3\\" class=\\"edge\\">\\n<title>c&#45;&gt;a</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\\" />\\n</g>\\n</g>\\n</svg>\\n</div>\\n\\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\\n\\n<h4 id=\\"q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</h4>\\n\\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 呢?方法如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}{%{% endraw %} raw %}\\n{% raw %}{%{% endraw %} endraw %}\\n</code></pre></div></div>\\n\\n<p>如上,就是用 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 把 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 包起来,但是 <code class=\\"language-plaintext highlighter-rouge\\">%}</code> 不用包。应该讲的很清楚了吧。</p>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<ol>\\n <li><a href=\\"https://bundler.io\\">https://bundler.io</a></li>\\n <li><a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></li>\\n <li><a href=\\"https://zhuanlan.zhihu.com/p/87225594\\">https://zhuanlan.zhihu.com/p/87225594</a></li>\\n <li><a href=\\"https://chat.openai.com/chat\\">https://chat.openai.com/chat</a></li>\\n <li><a href=\\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\\n <li><a href=\\"https://github.com/dyutibarma/monochrome\\">https://github.com/dyutibarma/monochrome</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\\n <li><a href=\\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\\n <li><a href=\\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"Github Pages":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\\n \\t<meta name=\\"description\\" content=\\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-21T15:53:57+00:00\\" class=\\"by-line\\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#写在前面\\" id=\\"markdown-toc-写在前面\\">写在前面</a></li>\\n <li><a href=\\"#1github上的准备\\" id=\\"markdown-toc-1github上的准备\\">1、GitHub 上的准备</a></li>\\n <li><a href=\\"#2了解ruby和jekyll\\" id=\\"markdown-toc-2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</a></li>\\n <li><a href=\\"#3了解gem\\" id=\\"markdown-toc-3了解gem\\">3、了解 Gem</a></li>\\n <li><a href=\\"#4安装homebrew\\" id=\\"markdown-toc-4安装homebrew\\">4、安装 Homebrew</a></li>\\n <li><a href=\\"#5用homebrew安装ruby\\" id=\\"markdown-toc-5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</a></li>\\n <li><a href=\\"#6安装jekyll和bundler\\" id=\\"markdown-toc-6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</a></li>\\n <li><a href=\\"#7使用bundle管理包依赖关系\\" id=\\"markdown-toc-7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</a></li>\\n <li><a href=\\"#8本地启动一下看看\\" id=\\"markdown-toc-8本地启动一下看看\\">8、本地启动一下看看</a></li>\\n <li><a href=\\"#9用jekyll创建一个项目\\" id=\\"markdown-toc-9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</a></li>\\n <li><a href=\\"#10修改gemfile文件\\" id=\\"markdown-toc-10修改gemfile文件\\">10、修改 Gemfile 文件</a></li>\\n <li><a href=\\"#11配置githubpages\\" id=\\"markdown-toc-11配置githubpages\\">11、配置 Github Pages</a></li>\\n <li><a href=\\"#12配置一个jekylltheme\\" id=\\"markdown-toc-12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</a></li>\\n <li><a href=\\"#13设置自定义域名\\" id=\\"markdown-toc-13设置自定义域名\\">13、设置自定义域名</a></li>\\n <li><a href=\\"#14用-rouge-实现代码高亮\\" id=\\"markdown-toc-14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</a></li>\\n <li><a href=\\"#15一些扩展问题\\" id=\\"markdown-toc-15一些扩展问题\\">15、一些扩展问题</a> <ul>\\n <li><a href=\\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\" id=\\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\\n <li><a href=\\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\" id=\\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\\n <li><a href=\\"#q3如何为每篇文章添加一个目录\\" id=\\"markdown-toc-q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</a></li>\\n <li><a href=\\"#q4如何在-jekyll-中支持-katex\\" id=\\"markdown-toc-q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\\n <li><a href=\\"#在-githubio-上\\" id=\\"markdown-toc-在-githubio-上\\">在 GitHub.io 上</a></li>\\n <li><a href=\\"#如果不在-githubio-上则还需要额外工作\\" id=\\"markdown-toc-如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\\n <li><a href=\\"#使用示例\\" id=\\"markdown-toc-使用示例\\">使用示例</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#q5jekyll-中如何支持-graphviz-\\" id=\\"markdown-toc-q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\\n <li><a href=\\"#q6如何显示--或者--\\" id=\\"markdown-toc-q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#参考\\" id=\\"markdown-toc-参考\\">参考</a></li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\\n\\n<ul>\\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\\n</ul>\\n\\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\\n\\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\\n\\n<h3 id=\\"1github上的准备\\">1、GitHub 上的准备</h3>\\n\\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git clone https://github.com/username/username.github.io\\n</code></pre></div></div>\\n\\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git add <span class=\\"nt\\">--all</span>\\n<span class=\\"nv\\">$ </span>git commit <span class=\\"nt\\">-m</span> <span class=\\"s2\\">\\"Initial commit\\"</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</h3>\\n\\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\\n\\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\\n\\n<h3 id=\\"3了解gem\\">3、了解 Gem</h3>\\n\\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\\n\\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\\ngem sources -l\\n</code></pre></div></div>\\n\\n<h3 id=\\"4安装homebrew\\">4、安装 Homebrew</h3>\\n\\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>/bin/bash <span class=\\"nt\\">-c</span> <span class=\\"s2\\">\\"</span><span class=\\"si\\">$(</span>curl <span class=\\"nt\\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\\"si\\">)</span><span class=\\"s2\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\\n\\n<h3 id=\\"5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</h3>\\n\\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>brew <span class=\\"nb\\">install </span>chruby ruby-install xz\\n</code></pre></div></div>\\n\\n<p>安装 Ruby 的最新版本:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby-install ruby\\n</code></pre></div></div>\\n\\n<p>这时候提示如下问题:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"o\\">&gt;&gt;&gt;</span> Updating ruby versions ...\\n<span class=\\"o\\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\\"se\\">\\\\</span>\\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\\n<span class=\\"o\\">!!!</span> Failed to download ruby versions!\\n</code></pre></div></div>\\n\\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ echo \\"185.199.111.133 raw.githubusercontent.com\\" &gt;&gt; /etc/hosts\\n</code></pre></div></div>\\n\\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/chruby.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/auto.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"chruby ruby-3.1.2\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc <span class=\\"c\\"># run 'chruby' to see actual version</span>\\n</code></pre></div></div>\\n\\n<p>再看下 Ruby 版本对不对:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby <span class=\\"nt\\">-v</span>\\n</code></pre></div></div>\\n\\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\\n\\n<h3 id=\\"6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"nb\\">install </span>jekyll bundler\\n</code></pre></div></div>\\n\\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\\n\\n<h3 id=\\"7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</h3>\\n\\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">source</span> <span class=\\"s1\\">'https://rubygems.org'</span>\\ngem <span class=\\"s1\\">'nokogiri'</span>\\ngem <span class=\\"s1\\">'rack'</span>, <span class=\\"s1\\">'~&gt; 2.2.4'</span>\\ngem <span class=\\"s1\\">'rspec'</span>\\ngem <span class=\\"s1\\">'jekyll'</span>\\n</code></pre></div></div>\\n\\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ git add Gemfile Gemfile.lock\\n</code></pre></div></div>\\n\\n<h3 id=\\"8本地启动一下看看\\">8、本地启动一下看看</h3>\\n\\n<p>先用 bundle 如下命令来启动:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: none\\n Source: /Users/captain/Workspace/poechant.github.io\\n Destination: /Users/captain/Workspace/poechant.github.io/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n done in 0.014 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\\n Server address: http://127.0.0.1:4000\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git pull <span class=\\"nt\\">--no-rebase</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll new CaptainMikeBlog\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>CaptainMikeBlog\\n<span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n Jekyll Feed: Generating feed for posts\\n done in 0.365 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\\n Server address: http://127.0.0.1:4000/\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"10修改gemfile文件\\">10、修改 Gemfile 文件</h3>\\n\\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"s2\\">\\"github-pages\\"</span>, <span class=\\"s2\\">\\"~&gt; GITHUB-PAGES-VERSION\\"</span>, group: :jekyll_plugins\\n</code></pre></div></div>\\n\\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ bundle install\\n</code></pre></div></div>\\n\\n<p>再本地启动服务器测试:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>得到如下提示:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\\nPrepending <span class=\\"sb\\">`</span>bundle <span class=\\"nb\\">exec</span><span class=\\"sb\\">`</span> to your <span class=\\"nb\\">command </span>may solve this. <span class=\\"o\\">(</span>Gem::LoadError<span class=\\"o\\">)</span>\\n</code></pre></div></div>\\n\\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle add webrick\\n<span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\\n\\n<h3 id=\\"11配置githubpages\\">11、配置 Github Pages</h3>\\n\\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>baseurl: \\"\\"\\nurl: \\"http://your-username.github.io\\"\\n</code></pre></div></div>\\n\\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\\n\\n<h3 id=\\"12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</h3>\\n\\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_includes\\n_layouts\\n_sass\\ncss\\njs\\nimg\\n404.markdown\\nindex.html\\n</code></pre></div></div>\\n\\n<p>不同主题会有所不同,这里只列个大概。</p>\\n\\n<h3 id=\\"13设置自定义域名\\">13、设置自定义域名</h3>\\n\\n<p>添加四条 A 记录,记录值如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>185.199.108.153\\n185.199.109.153\\n185.199.110.153\\n185.199.111.153\\n</code></pre></div></div>\\n\\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\\n\\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\\n\\n<h3 id=\\"14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</h3>\\n\\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem install kramdom rouge\\n</code></pre></div></div>\\n\\n<p>然后配置 _config.yml 文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>然后用 rouge 创建 syntax.css 文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>rougify style github <span class=\\"o\\">&gt;</span> css/syntax.css\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">_include/head.html</code> 文件中添加:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/css/syntax.css\\"</span> <span class=\\"nt\\">/&gt;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"15一些扩展问题\\">15、一些扩展问题</h3>\\n\\n<h4 id=\\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\\n\\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是一篇文章</span>\\n<span class=\\"na\\">excerpt</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是文章的摘要</span>\\n<span class=\\"nn\\">---</span>\\n\\n这是文章的正文内容\\n</code></pre></div></div>\\n\\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;ul&gt;</span>\\n {% for post in paginator.posts %}\\n <span class=\\"nt\\">&lt;li&gt;</span>\\n <span class=\\"nt\\">&lt;h2&gt;&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{{ post.url }}\\"</span><span class=\\"nt\\">&gt;</span>{{ post.title }}<span class=\\"nt\\">&lt;/a&gt;&lt;/h2&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>{{ post.excerpt }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n <span class=\\"nt\\">&lt;/li&gt;</span>\\n {% endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\\n\\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\\n\\n<h4 id=\\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\\n\\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code>目录下创建一个<code class=\\"language-plaintext highlighter-rouge\\">category.html</code>文件:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>---\\nlayout: default\\n---\\n\\n<span class=\\"nt\\">&lt;div</span> <span class=\\"na\\">class=</span><span class=\\"s\\">\\"container\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n {% if site.categories[page.category] %}\\n {% for post in site.categories[page.category] %}\\n <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{% if site.baseurl == \\"</span><span class=\\"err\\">/\\"</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">else</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">|</span> <span class=\\"na\\">prepend:</span> <span class=\\"na\\">site.baseurl</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">endif</span> <span class=\\"err\\">%}\\"</span><span class=\\"nt\\">&gt;</span>\\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\\n <span class=\\"nt\\">&lt;/a&gt;</span>\\n {% endfor %}\\n {% else %}\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>No posts for this category. If you have something in mind, check <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/write\\"</span><span class=\\"nt\\">&gt;</span>Write For Us<span class=\\"nt\\">&lt;/a&gt;</span>page.<span class=\\"nt\\">&lt;/p&gt;</span>\\n {% endif %}\\n<span class=\\"nt\\">&lt;/div&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">include</span><span class=\\"pi\\">:</span> <span class=\\"pi\\">[</span><span class=\\"s1\\">'</span><span class=\\"s\\">_categories'</span><span class=\\"pi\\">]</span>\\n</code></pre></div></div>\\n\\n<p>在根目录创建一个<code class=\\"language-plaintext highlighter-rouge\\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\\"language-plaintext highlighter-rouge\\">ai.html</code>如下:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">category</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">人工智能</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s\\">This is the description.</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/category/ai</span>\\n<span class=\\"na\\">category</span><span class=\\"pi\\">:</span> <span class=\\"s\\">ai</span>\\n<span class=\\"na\\">category_type</span><span class=\\"pi\\">:</span> <span class=\\"s\\">tech</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>那么之后每次创建文件时,在头部写<code class=\\"language-plaintext highlighter-rouge\\">category</code>一定要与这些<code class=\\"language-plaintext highlighter-rouge\\">categories</code>中的<code class=\\"language-plaintext highlighter-rouge\\">html</code>文件对应起来。</p>\\n\\n<h4 id=\\"q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</h4>\\n\\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">*</span> TOC\\n{:toc}\\n</code></pre></div></div>\\n\\n<h4 id=\\"q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\\n\\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\\n\\n<h5 id=\\"在-githubio-上\\">在 GitHub.io 上</h5>\\n\\n<p>先修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">math_engine</span><span class=\\"pi\\">:</span> <span class=\\"s\\">katex</span>\\n</code></pre></div></div>\\n\\n<p>然后修改 <code class=\\"language-plaintext highlighter-rouge\\">_includes/head.html</code> 文件,在 <code class=\\"language-plaintext highlighter-rouge\\">&lt;head&gt;</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">&lt;/head&gt;</code> 中间:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\">&lt;!--KaTeX--&gt;</span>\\n <span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span>\\n <span class=\\"na\\">href=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script&gt;</span>\\n <span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">addEventListener</span><span class=\\"p\\">(</span><span class=\\"dl\\">\\"</span><span class=\\"s2\\">DOMContentLoaded</span><span class=\\"dl\\">\\"</span><span class=\\"p\\">,</span> <span class=\\"kd\\">function</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"nx\\">renderMathInElement</span><span class=\\"p\\">(</span><span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">body</span><span class=\\"p\\">,</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// ...options...</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"nt\\">&lt;/script&gt;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</h5>\\n\\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem <span class=\\"nb\\">install </span>kramdom-math-katex\\n\\ngem <span class=\\"nb\\">install </span>katex\\ngem <span class=\\"nb\\">install </span>execjs\\n\\ngem <span class=\\"nb\\">install </span>therubyracer\\ngem <span class=\\"nb\\">install </span>therubyrhino\\ngem <span class=\\"nb\\">install </span>duktape\\n</code></pre></div></div>\\n\\n<h5 id=\\"使用示例\\">使用示例</h5>\\n\\n<p>以如下方式输入输入如下内容:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}\\n$$ \\\\sum_{i=1}^{n} a_i $$\\n{% endraw %}\\n</code></pre></div></div>\\n\\n<p>就会得到一个数学公式:</p>\\n\\n\\\\[\\\\sum_{i=1}^{n} a_i\\\\]\\n\\n<h4 id=\\"q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\\n\\n<p>这要依赖 <code class=\\"language-plaintext highlighter-rouge\\">jekyll-graphviz-dot</code>,修改 <code class=\\"language-plaintext highlighter-rouge\\">Gemfile</code> 增加一句:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>group :jekyll_plugins <span class=\\"k\\">do\\n </span>gem <span class=\\"s2\\">\\"jekyll-graphviz-dot\\"</span>\\nend\\n</code></pre></div></div>\\n\\n<p>再修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 配置文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-graphviz</span>\\n</code></pre></div></div>\\n\\n<p>再在本地安装 graphviz,可以通过 <code class=\\"language-plaintext highlighter-rouge\\">conda install graphviz</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">brew install graphviz</code>。然后 <code class=\\"language-plaintext highlighter-rouge\\">bundle install</code> 再 <code class=\\"language-plaintext highlighter-rouge\\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\\n\\n<pre><code class=\\"language-graphviz\\">{% graph some graph title %}\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n{% endgraph %}\\n</code></pre>\\n\\n<p>如果看到如下效果,就说明你都配置成功了:</p>\\n\\n<div class=\\"graphviz-wrapper\\">\\n\\n<!-- Generated by graphviz version 2.43.0 (0)\\n -->\\n<!-- Title: G Pages: 1 -->\\n<svg role=\\"img\\" aria-label=\\"some graph title\\" width=\\"89pt\\" height=\\"188pt\\" viewBox=\\"0.00 0.00 89.00 188.00\\">\\n<title>some graph title</title>\\n<desc>\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n</desc>\\n\\n<g id=\\"graph0\\" class=\\"graph\\" transform=\\"scale(1 1) rotate(0) translate(4 184)\\">\\n<title>G</title>\\n<polygon fill=\\"white\\" stroke=\\"transparent\\" points=\\"-4,4 -4,-184 85,-184 85,4 -4,4\\" />\\n<!-- a -->\\n<g id=\\"node1\\" class=\\"node\\">\\n<title>a</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-162\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-158.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">a</text>\\n</g>\\n<!-- b -->\\n<g id=\\"node2\\" class=\\"node\\">\\n<title>b</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"27\\" cy=\\"-90\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"27\\" y=\\"-86.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">b</text>\\n</g>\\n<!-- a&#45;&gt;b -->\\n<g id=\\"edge1\\" class=\\"edge\\">\\n<title>a&#45;&gt;b</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\\" />\\n</g>\\n<!-- c -->\\n<g id=\\"node3\\" class=\\"node\\">\\n<title>c</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-18\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-14.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">c</text>\\n</g>\\n<!-- b&#45;&gt;c -->\\n<g id=\\"edge2\\" class=\\"edge\\">\\n<title>b&#45;&gt;c</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\\" />\\n</g>\\n<!-- c&#45;&gt;a -->\\n<g id=\\"edge3\\" class=\\"edge\\">\\n<title>c&#45;&gt;a</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\\" />\\n</g>\\n</g>\\n</svg>\\n</div>\\n\\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\\n\\n<h4 id=\\"q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</h4>\\n\\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 呢?方法如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}{%{% endraw %} raw %}\\n{% raw %}{%{% endraw %} endraw %}\\n</code></pre></div></div>\\n\\n<p>如上,就是用 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 把 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 包起来,但是 <code class=\\"language-plaintext highlighter-rouge\\">%}</code> 不用包。应该讲的很清楚了吧。</p>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<ol>\\n <li><a href=\\"https://bundler.io\\">https://bundler.io</a></li>\\n <li><a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></li>\\n <li><a href=\\"https://zhuanlan.zhihu.com/p/87225594\\">https://zhuanlan.zhihu.com/p/87225594</a></li>\\n <li><a href=\\"https://chat.openai.com/chat\\">https://chat.openai.com/chat</a></li>\\n <li><a href=\\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\\n <li><a href=\\"https://github.com/dyutibarma/monochrome\\">https://github.com/dyutibarma/monochrome</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\\n <li><a href=\\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\\n <li><a href=\\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"前端":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的 Jekyll 快速教程</title>\\n \\t<meta name=\\"description\\" content=\\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的 Jekyll 快速教程</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-23T19:43:02+00:00\\" class=\\"by-line\\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\\n\\n<h3 id=\\"part-1基本特点\\">Part 1、基本特点</h3>\\n\\n<h4 id=\\"一基本语法\\">一、基本语法</h4>\\n\\n<ul>\\n <li>变量:用双大括号表示变量 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code></li>\\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\\n <li>支持循环与分支结构:比如 <code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">if-elsif-else-endif</code> :可以使用 <code class=\\"language-plaintext highlighter-rouge\\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\\n</ul>\\n\\n<h4 id=\\"二典型-jekyll-项目结构及重要文件介绍\\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\\n\\n<h5 id=\\"1配置文件-_configyml\\">1、配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code></h5>\\n\\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\\"language-plaintext highlighter-rouge\\">encoding: utf-8</code> 这一条。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Site settings</span>\\n<span class=\\"na\\">encoding</span><span class=\\"pi\\">:</span> <span class=\\"s\\">utf-8</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">麦克船长的技术、产品与商业博客</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\\"</span>\\n<span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptain.com\\"</span>\\n<span class=\\"na\\">author</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">name</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Your</span><span class=\\"nv\\"> </span><span class=\\"s\\">Name\\"</span>\\n <span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptian.com\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Markdown and highlighter</span>\\n<span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Plugins</span>\\n<span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-paginate</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-sitemap</span>\\n</code></pre></div></div>\\n\\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Build settings</span>\\n<span class=\\"na\\">baseurl</span><span class=\\"pi\\">:</span> <span class=\\"c1\\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\\n<span class=\\"na\\">source</span><span class=\\"pi\\">:</span> <span class=\\"s\\">.</span>\\n<span class=\\"na\\">destination</span><span class=\\"pi\\">:</span> <span class=\\"s\\">./_site</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:title</span>\\n<span class=\\"na\\">paginate</span><span class=\\"pi\\">:</span> <span class=\\"m\\">20</span>\\n<span class=\\"na\\">paginate_path</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/page:num/</span>\\n<span class=\\"na\\">collections</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">posts</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">output</span><span class=\\"pi\\">:</span> <span class=\\"no\\">true</span>\\n <span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:year/:month/:day/:title/</span>\\n <span class=\\"na\\">directory</span><span class=\\"pi\\">:</span> <span class=\\"s\\">_posts</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2布局文件_layouts-目录下的文件规则\\">2、布局文件:<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的文件规则</h5>\\n\\n<p>Jekyll 的 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\\"language-plaintext highlighter-rouge\\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\\"language-plaintext highlighter-rouge\\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\\n\\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\\n\\n<h5 id=\\"3页面文件及其头部\\">3、页面文件及其头部</h5>\\n\\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">page</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/categories/</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">Categories</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>每个页面文件的头部都会有layout,并与 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的某个文件对应。</p>\\n\\n<h3 id=\\"part-2jekyll-中的全局变量\\">Part 2、Jekyll 中的全局变量</h3>\\n\\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:包含当前页面或文章的内容。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:包含有关网站的所有标签的信息。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\\n</ul>\\n\\n<p>这些变量可以在模板中使用,比如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;h1&gt;</span>{{ page.title }}<span class=\\"nt\\">&lt;/h1&gt;</span>\\n<span class=\\"nt\\">&lt;p&gt;</span>{{ site.description }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n<span class=\\"nt\\">&lt;ul&gt;</span>\\n {{ for category in site.categories %}\\n <span class=\\"nt\\">&lt;li&gt;</span>{{ category }}<span class=\\"nt\\">&lt;/li&gt;</span>\\n {{ endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span> \\n</code></pre></div></div>\\n\\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">my_custom_variable</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Hello</span><span class=\\"nv\\"> </span><span class=\\"s\\">World\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后就可以在模板文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\\n\\n<h4 id=\\"一site变量\\">一、site变量</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\\n\\n<h5 id=\\"1sitecategories\\">1、<code class=\\"language-plaintext highlighter-rouge\\">site.categories</code></h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\\"language-plaintext highlighter-rouge\\">first</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">category</code> 的名字,如下使用:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2sitepages\\">2、site.pages</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3其他常用属性\\">3、其他常用属性</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.title</code>:是网站的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.related_posts</code>:相关文章的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.data</code>:从 <code class=\\"language-plaintext highlighter-rouge\\">_data</code> 目录加载的数据。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.static_files</code>:静态文件的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.collections</code>:自定义集合的列表。</li>\\n</ul>\\n\\n<h4 id=\\"二page-变量\\">二、<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量</h4>\\n\\n<p>在 Jekyll 中,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量属性包括:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">layout</code>:表示页面使用的布局模板的名称。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">title</code>:表示页面的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">date</code>:表示页面的发布日期。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">categories</code>:表示页面所属的分类列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:表示页面所属的标签列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\\n</ul>\\n\\n<p>在模板中,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.属性名 }}</code> 的方式来访问 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.title }}</code>。此外,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量还有其他属性,如 <code class=\\"language-plaintext highlighter-rouge\\">permalink</code>、<code class=\\"language-plaintext highlighter-rouge\\">excerpt</code>、<code class=\\"language-plaintext highlighter-rouge\\">url</code> 等,可以根据需要调用。</p>\\n\\n<h3 id=\\"part-3控制结构\\">Part 3、控制结构</h3>\\n\\n<h4 id=\\"1if-else-分支结构\\">1、<code class=\\"language-plaintext highlighter-rouge\\">if-else</code> 分支结构</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ if tmp_var == \\"type1\\" %}\\n{{ elsif tmp_var == \\"type2\\" %}\\n{{ elsif tmp_var == \\"type3\\" %}\\n{{ elsif tmp_var == \\"type4\\" %}\\n{{ else tmp_var == \\"type5\\" %}\\n{{ endif %}\\n</code></pre></div></div>\\n\\n<h4 id=\\"2for-endfor-循环结构\\">2、<code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 循环结构</h4>\\n\\n<p>不带条件判断的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 循环如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for post in paginator.posts %}\\n\\t<span class=\\"c\\">&lt;!-- Your other sentences --&gt;</span>\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<p>带条件循环的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages | where: \\"dir\\", \\"categories\\" %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3jekyll-支持的其他结构包括\\">3、Jekyll 支持的其他结构包括:</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">capture</code> 用于捕获输出的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">cycle</code> 用于循环一组字符串的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">include</code> 用于包含其他文件的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">while</code> 用于在满足指定条件时执行代码的结构。</li>\\n</ul>\\n\\n<h3 id=\\"参考\\">参考:</h3>\\n\\n<p>1、<a href=\\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\\n2、<a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\\n \\t<meta name=\\"description\\" content=\\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-21T15:53:57+00:00\\" class=\\"by-line\\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#写在前面\\" id=\\"markdown-toc-写在前面\\">写在前面</a></li>\\n <li><a href=\\"#1github上的准备\\" id=\\"markdown-toc-1github上的准备\\">1、GitHub 上的准备</a></li>\\n <li><a href=\\"#2了解ruby和jekyll\\" id=\\"markdown-toc-2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</a></li>\\n <li><a href=\\"#3了解gem\\" id=\\"markdown-toc-3了解gem\\">3、了解 Gem</a></li>\\n <li><a href=\\"#4安装homebrew\\" id=\\"markdown-toc-4安装homebrew\\">4、安装 Homebrew</a></li>\\n <li><a href=\\"#5用homebrew安装ruby\\" id=\\"markdown-toc-5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</a></li>\\n <li><a href=\\"#6安装jekyll和bundler\\" id=\\"markdown-toc-6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</a></li>\\n <li><a href=\\"#7使用bundle管理包依赖关系\\" id=\\"markdown-toc-7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</a></li>\\n <li><a href=\\"#8本地启动一下看看\\" id=\\"markdown-toc-8本地启动一下看看\\">8、本地启动一下看看</a></li>\\n <li><a href=\\"#9用jekyll创建一个项目\\" id=\\"markdown-toc-9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</a></li>\\n <li><a href=\\"#10修改gemfile文件\\" id=\\"markdown-toc-10修改gemfile文件\\">10、修改 Gemfile 文件</a></li>\\n <li><a href=\\"#11配置githubpages\\" id=\\"markdown-toc-11配置githubpages\\">11、配置 Github Pages</a></li>\\n <li><a href=\\"#12配置一个jekylltheme\\" id=\\"markdown-toc-12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</a></li>\\n <li><a href=\\"#13设置自定义域名\\" id=\\"markdown-toc-13设置自定义域名\\">13、设置自定义域名</a></li>\\n <li><a href=\\"#14用-rouge-实现代码高亮\\" id=\\"markdown-toc-14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</a></li>\\n <li><a href=\\"#15一些扩展问题\\" id=\\"markdown-toc-15一些扩展问题\\">15、一些扩展问题</a> <ul>\\n <li><a href=\\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\" id=\\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\\n <li><a href=\\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\" id=\\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\\n <li><a href=\\"#q3如何为每篇文章添加一个目录\\" id=\\"markdown-toc-q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</a></li>\\n <li><a href=\\"#q4如何在-jekyll-中支持-katex\\" id=\\"markdown-toc-q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\\n <li><a href=\\"#在-githubio-上\\" id=\\"markdown-toc-在-githubio-上\\">在 GitHub.io 上</a></li>\\n <li><a href=\\"#如果不在-githubio-上则还需要额外工作\\" id=\\"markdown-toc-如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\\n <li><a href=\\"#使用示例\\" id=\\"markdown-toc-使用示例\\">使用示例</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#q5jekyll-中如何支持-graphviz-\\" id=\\"markdown-toc-q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\\n <li><a href=\\"#q6如何显示--或者--\\" id=\\"markdown-toc-q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#参考\\" id=\\"markdown-toc-参考\\">参考</a></li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\\n\\n<ul>\\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\\n</ul>\\n\\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\\n\\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\\n\\n<h3 id=\\"1github上的准备\\">1、GitHub 上的准备</h3>\\n\\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git clone https://github.com/username/username.github.io\\n</code></pre></div></div>\\n\\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git add <span class=\\"nt\\">--all</span>\\n<span class=\\"nv\\">$ </span>git commit <span class=\\"nt\\">-m</span> <span class=\\"s2\\">\\"Initial commit\\"</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</h3>\\n\\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\\n\\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\\n\\n<h3 id=\\"3了解gem\\">3、了解 Gem</h3>\\n\\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\\n\\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\\ngem sources -l\\n</code></pre></div></div>\\n\\n<h3 id=\\"4安装homebrew\\">4、安装 Homebrew</h3>\\n\\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>/bin/bash <span class=\\"nt\\">-c</span> <span class=\\"s2\\">\\"</span><span class=\\"si\\">$(</span>curl <span class=\\"nt\\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\\"si\\">)</span><span class=\\"s2\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\\n\\n<h3 id=\\"5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</h3>\\n\\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>brew <span class=\\"nb\\">install </span>chruby ruby-install xz\\n</code></pre></div></div>\\n\\n<p>安装 Ruby 的最新版本:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby-install ruby\\n</code></pre></div></div>\\n\\n<p>这时候提示如下问题:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"o\\">&gt;&gt;&gt;</span> Updating ruby versions ...\\n<span class=\\"o\\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\\"se\\">\\\\</span>\\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\\n<span class=\\"o\\">!!!</span> Failed to download ruby versions!\\n</code></pre></div></div>\\n\\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ echo \\"185.199.111.133 raw.githubusercontent.com\\" &gt;&gt; /etc/hosts\\n</code></pre></div></div>\\n\\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/chruby.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/auto.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"chruby ruby-3.1.2\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc <span class=\\"c\\"># run 'chruby' to see actual version</span>\\n</code></pre></div></div>\\n\\n<p>再看下 Ruby 版本对不对:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby <span class=\\"nt\\">-v</span>\\n</code></pre></div></div>\\n\\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\\n\\n<h3 id=\\"6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"nb\\">install </span>jekyll bundler\\n</code></pre></div></div>\\n\\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\\n\\n<h3 id=\\"7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</h3>\\n\\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">source</span> <span class=\\"s1\\">'https://rubygems.org'</span>\\ngem <span class=\\"s1\\">'nokogiri'</span>\\ngem <span class=\\"s1\\">'rack'</span>, <span class=\\"s1\\">'~&gt; 2.2.4'</span>\\ngem <span class=\\"s1\\">'rspec'</span>\\ngem <span class=\\"s1\\">'jekyll'</span>\\n</code></pre></div></div>\\n\\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ git add Gemfile Gemfile.lock\\n</code></pre></div></div>\\n\\n<h3 id=\\"8本地启动一下看看\\">8、本地启动一下看看</h3>\\n\\n<p>先用 bundle 如下命令来启动:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: none\\n Source: /Users/captain/Workspace/poechant.github.io\\n Destination: /Users/captain/Workspace/poechant.github.io/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n done in 0.014 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\\n Server address: http://127.0.0.1:4000\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git pull <span class=\\"nt\\">--no-rebase</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll new CaptainMikeBlog\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>CaptainMikeBlog\\n<span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n Jekyll Feed: Generating feed for posts\\n done in 0.365 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\\n Server address: http://127.0.0.1:4000/\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"10修改gemfile文件\\">10、修改 Gemfile 文件</h3>\\n\\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"s2\\">\\"github-pages\\"</span>, <span class=\\"s2\\">\\"~&gt; GITHUB-PAGES-VERSION\\"</span>, group: :jekyll_plugins\\n</code></pre></div></div>\\n\\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ bundle install\\n</code></pre></div></div>\\n\\n<p>再本地启动服务器测试:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>得到如下提示:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\\nPrepending <span class=\\"sb\\">`</span>bundle <span class=\\"nb\\">exec</span><span class=\\"sb\\">`</span> to your <span class=\\"nb\\">command </span>may solve this. <span class=\\"o\\">(</span>Gem::LoadError<span class=\\"o\\">)</span>\\n</code></pre></div></div>\\n\\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle add webrick\\n<span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\\n\\n<h3 id=\\"11配置githubpages\\">11、配置 Github Pages</h3>\\n\\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>baseurl: \\"\\"\\nurl: \\"http://your-username.github.io\\"\\n</code></pre></div></div>\\n\\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\\n\\n<h3 id=\\"12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</h3>\\n\\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_includes\\n_layouts\\n_sass\\ncss\\njs\\nimg\\n404.markdown\\nindex.html\\n</code></pre></div></div>\\n\\n<p>不同主题会有所不同,这里只列个大概。</p>\\n\\n<h3 id=\\"13设置自定义域名\\">13、设置自定义域名</h3>\\n\\n<p>添加四条 A 记录,记录值如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>185.199.108.153\\n185.199.109.153\\n185.199.110.153\\n185.199.111.153\\n</code></pre></div></div>\\n\\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\\n\\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\\n\\n<h3 id=\\"14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</h3>\\n\\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem install kramdom rouge\\n</code></pre></div></div>\\n\\n<p>然后配置 _config.yml 文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>然后用 rouge 创建 syntax.css 文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>rougify style github <span class=\\"o\\">&gt;</span> css/syntax.css\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">_include/head.html</code> 文件中添加:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/css/syntax.css\\"</span> <span class=\\"nt\\">/&gt;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"15一些扩展问题\\">15、一些扩展问题</h3>\\n\\n<h4 id=\\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\\n\\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是一篇文章</span>\\n<span class=\\"na\\">excerpt</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是文章的摘要</span>\\n<span class=\\"nn\\">---</span>\\n\\n这是文章的正文内容\\n</code></pre></div></div>\\n\\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;ul&gt;</span>\\n {% for post in paginator.posts %}\\n <span class=\\"nt\\">&lt;li&gt;</span>\\n <span class=\\"nt\\">&lt;h2&gt;&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{{ post.url }}\\"</span><span class=\\"nt\\">&gt;</span>{{ post.title }}<span class=\\"nt\\">&lt;/a&gt;&lt;/h2&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>{{ post.excerpt }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n <span class=\\"nt\\">&lt;/li&gt;</span>\\n {% endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\\n\\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\\n\\n<h4 id=\\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\\n\\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code>目录下创建一个<code class=\\"language-plaintext highlighter-rouge\\">category.html</code>文件:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>---\\nlayout: default\\n---\\n\\n<span class=\\"nt\\">&lt;div</span> <span class=\\"na\\">class=</span><span class=\\"s\\">\\"container\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n {% if site.categories[page.category] %}\\n {% for post in site.categories[page.category] %}\\n <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{% if site.baseurl == \\"</span><span class=\\"err\\">/\\"</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">else</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">|</span> <span class=\\"na\\">prepend:</span> <span class=\\"na\\">site.baseurl</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">endif</span> <span class=\\"err\\">%}\\"</span><span class=\\"nt\\">&gt;</span>\\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\\n <span class=\\"nt\\">&lt;/a&gt;</span>\\n {% endfor %}\\n {% else %}\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>No posts for this category. If you have something in mind, check <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/write\\"</span><span class=\\"nt\\">&gt;</span>Write For Us<span class=\\"nt\\">&lt;/a&gt;</span>page.<span class=\\"nt\\">&lt;/p&gt;</span>\\n {% endif %}\\n<span class=\\"nt\\">&lt;/div&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">include</span><span class=\\"pi\\">:</span> <span class=\\"pi\\">[</span><span class=\\"s1\\">'</span><span class=\\"s\\">_categories'</span><span class=\\"pi\\">]</span>\\n</code></pre></div></div>\\n\\n<p>在根目录创建一个<code class=\\"language-plaintext highlighter-rouge\\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\\"language-plaintext highlighter-rouge\\">ai.html</code>如下:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">category</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">人工智能</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s\\">This is the description.</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/category/ai</span>\\n<span class=\\"na\\">category</span><span class=\\"pi\\">:</span> <span class=\\"s\\">ai</span>\\n<span class=\\"na\\">category_type</span><span class=\\"pi\\">:</span> <span class=\\"s\\">tech</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>那么之后每次创建文件时,在头部写<code class=\\"language-plaintext highlighter-rouge\\">category</code>一定要与这些<code class=\\"language-plaintext highlighter-rouge\\">categories</code>中的<code class=\\"language-plaintext highlighter-rouge\\">html</code>文件对应起来。</p>\\n\\n<h4 id=\\"q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</h4>\\n\\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">*</span> TOC\\n{:toc}\\n</code></pre></div></div>\\n\\n<h4 id=\\"q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\\n\\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\\n\\n<h5 id=\\"在-githubio-上\\">在 GitHub.io 上</h5>\\n\\n<p>先修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">math_engine</span><span class=\\"pi\\">:</span> <span class=\\"s\\">katex</span>\\n</code></pre></div></div>\\n\\n<p>然后修改 <code class=\\"language-plaintext highlighter-rouge\\">_includes/head.html</code> 文件,在 <code class=\\"language-plaintext highlighter-rouge\\">&lt;head&gt;</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">&lt;/head&gt;</code> 中间:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\">&lt;!--KaTeX--&gt;</span>\\n <span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span>\\n <span class=\\"na\\">href=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script&gt;</span>\\n <span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">addEventListener</span><span class=\\"p\\">(</span><span class=\\"dl\\">\\"</span><span class=\\"s2\\">DOMContentLoaded</span><span class=\\"dl\\">\\"</span><span class=\\"p\\">,</span> <span class=\\"kd\\">function</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"nx\\">renderMathInElement</span><span class=\\"p\\">(</span><span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">body</span><span class=\\"p\\">,</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// ...options...</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"nt\\">&lt;/script&gt;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</h5>\\n\\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem <span class=\\"nb\\">install </span>kramdom-math-katex\\n\\ngem <span class=\\"nb\\">install </span>katex\\ngem <span class=\\"nb\\">install </span>execjs\\n\\ngem <span class=\\"nb\\">install </span>therubyracer\\ngem <span class=\\"nb\\">install </span>therubyrhino\\ngem <span class=\\"nb\\">install </span>duktape\\n</code></pre></div></div>\\n\\n<h5 id=\\"使用示例\\">使用示例</h5>\\n\\n<p>以如下方式输入输入如下内容:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}\\n$$ \\\\sum_{i=1}^{n} a_i $$\\n{% endraw %}\\n</code></pre></div></div>\\n\\n<p>就会得到一个数学公式:</p>\\n\\n\\\\[\\\\sum_{i=1}^{n} a_i\\\\]\\n\\n<h4 id=\\"q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\\n\\n<p>这要依赖 <code class=\\"language-plaintext highlighter-rouge\\">jekyll-graphviz-dot</code>,修改 <code class=\\"language-plaintext highlighter-rouge\\">Gemfile</code> 增加一句:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>group :jekyll_plugins <span class=\\"k\\">do\\n </span>gem <span class=\\"s2\\">\\"jekyll-graphviz-dot\\"</span>\\nend\\n</code></pre></div></div>\\n\\n<p>再修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 配置文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-graphviz</span>\\n</code></pre></div></div>\\n\\n<p>再在本地安装 graphviz,可以通过 <code class=\\"language-plaintext highlighter-rouge\\">conda install graphviz</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">brew install graphviz</code>。然后 <code class=\\"language-plaintext highlighter-rouge\\">bundle install</code> 再 <code class=\\"language-plaintext highlighter-rouge\\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\\n\\n<pre><code class=\\"language-graphviz\\">{% graph some graph title %}\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n{% endgraph %}\\n</code></pre>\\n\\n<p>如果看到如下效果,就说明你都配置成功了:</p>\\n\\n<div class=\\"graphviz-wrapper\\">\\n\\n<!-- Generated by graphviz version 2.43.0 (0)\\n -->\\n<!-- Title: G Pages: 1 -->\\n<svg role=\\"img\\" aria-label=\\"some graph title\\" width=\\"89pt\\" height=\\"188pt\\" viewBox=\\"0.00 0.00 89.00 188.00\\">\\n<title>some graph title</title>\\n<desc>\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n</desc>\\n\\n<g id=\\"graph0\\" class=\\"graph\\" transform=\\"scale(1 1) rotate(0) translate(4 184)\\">\\n<title>G</title>\\n<polygon fill=\\"white\\" stroke=\\"transparent\\" points=\\"-4,4 -4,-184 85,-184 85,4 -4,4\\" />\\n<!-- a -->\\n<g id=\\"node1\\" class=\\"node\\">\\n<title>a</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-162\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-158.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">a</text>\\n</g>\\n<!-- b -->\\n<g id=\\"node2\\" class=\\"node\\">\\n<title>b</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"27\\" cy=\\"-90\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"27\\" y=\\"-86.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">b</text>\\n</g>\\n<!-- a&#45;&gt;b -->\\n<g id=\\"edge1\\" class=\\"edge\\">\\n<title>a&#45;&gt;b</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\\" />\\n</g>\\n<!-- c -->\\n<g id=\\"node3\\" class=\\"node\\">\\n<title>c</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-18\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-14.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">c</text>\\n</g>\\n<!-- b&#45;&gt;c -->\\n<g id=\\"edge2\\" class=\\"edge\\">\\n<title>b&#45;&gt;c</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\\" />\\n</g>\\n<!-- c&#45;&gt;a -->\\n<g id=\\"edge3\\" class=\\"edge\\">\\n<title>c&#45;&gt;a</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\\" />\\n</g>\\n</g>\\n</svg>\\n</div>\\n\\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\\n\\n<h4 id=\\"q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</h4>\\n\\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 呢?方法如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}{%{% endraw %} raw %}\\n{% raw %}{%{% endraw %} endraw %}\\n</code></pre></div></div>\\n\\n<p>如上,就是用 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 把 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 包起来,但是 <code class=\\"language-plaintext highlighter-rouge\\">%}</code> 不用包。应该讲的很清楚了吧。</p>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<ol>\\n <li><a href=\\"https://bundler.io\\">https://bundler.io</a></li>\\n <li><a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></li>\\n <li><a href=\\"https://zhuanlan.zhihu.com/p/87225594\\">https://zhuanlan.zhihu.com/p/87225594</a></li>\\n <li><a href=\\"https://chat.openai.com/chat\\">https://chat.openai.com/chat</a></li>\\n <li><a href=\\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\\n <li><a href=\\"https://github.com/dyutibarma/monochrome\\">https://github.com/dyutibarma/monochrome</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\\n <li><a href=\\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\\n <li><a href=\\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"Web":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的 Jekyll 快速教程</title>\\n \\t<meta name=\\"description\\" content=\\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的 Jekyll 快速教程</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-23T19:43:02+00:00\\" class=\\"by-line\\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\\n\\n<h3 id=\\"part-1基本特点\\">Part 1、基本特点</h3>\\n\\n<h4 id=\\"一基本语法\\">一、基本语法</h4>\\n\\n<ul>\\n <li>变量:用双大括号表示变量 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code></li>\\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\\n <li>支持循环与分支结构:比如 <code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">if-elsif-else-endif</code> :可以使用 <code class=\\"language-plaintext highlighter-rouge\\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\\n</ul>\\n\\n<h4 id=\\"二典型-jekyll-项目结构及重要文件介绍\\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\\n\\n<h5 id=\\"1配置文件-_configyml\\">1、配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code></h5>\\n\\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\\"language-plaintext highlighter-rouge\\">encoding: utf-8</code> 这一条。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Site settings</span>\\n<span class=\\"na\\">encoding</span><span class=\\"pi\\">:</span> <span class=\\"s\\">utf-8</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">麦克船长的技术、产品与商业博客</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\\"</span>\\n<span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptain.com\\"</span>\\n<span class=\\"na\\">author</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">name</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Your</span><span class=\\"nv\\"> </span><span class=\\"s\\">Name\\"</span>\\n <span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptian.com\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Markdown and highlighter</span>\\n<span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Plugins</span>\\n<span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-paginate</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-sitemap</span>\\n</code></pre></div></div>\\n\\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Build settings</span>\\n<span class=\\"na\\">baseurl</span><span class=\\"pi\\">:</span> <span class=\\"c1\\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\\n<span class=\\"na\\">source</span><span class=\\"pi\\">:</span> <span class=\\"s\\">.</span>\\n<span class=\\"na\\">destination</span><span class=\\"pi\\">:</span> <span class=\\"s\\">./_site</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:title</span>\\n<span class=\\"na\\">paginate</span><span class=\\"pi\\">:</span> <span class=\\"m\\">20</span>\\n<span class=\\"na\\">paginate_path</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/page:num/</span>\\n<span class=\\"na\\">collections</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">posts</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">output</span><span class=\\"pi\\">:</span> <span class=\\"no\\">true</span>\\n <span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:year/:month/:day/:title/</span>\\n <span class=\\"na\\">directory</span><span class=\\"pi\\">:</span> <span class=\\"s\\">_posts</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2布局文件_layouts-目录下的文件规则\\">2、布局文件:<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的文件规则</h5>\\n\\n<p>Jekyll 的 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\\"language-plaintext highlighter-rouge\\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\\"language-plaintext highlighter-rouge\\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\\n\\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\\n\\n<h5 id=\\"3页面文件及其头部\\">3、页面文件及其头部</h5>\\n\\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">page</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/categories/</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">Categories</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>每个页面文件的头部都会有layout,并与 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的某个文件对应。</p>\\n\\n<h3 id=\\"part-2jekyll-中的全局变量\\">Part 2、Jekyll 中的全局变量</h3>\\n\\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:包含当前页面或文章的内容。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:包含有关网站的所有标签的信息。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\\n</ul>\\n\\n<p>这些变量可以在模板中使用,比如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;h1&gt;</span>{{ page.title }}<span class=\\"nt\\">&lt;/h1&gt;</span>\\n<span class=\\"nt\\">&lt;p&gt;</span>{{ site.description }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n<span class=\\"nt\\">&lt;ul&gt;</span>\\n {{ for category in site.categories %}\\n <span class=\\"nt\\">&lt;li&gt;</span>{{ category }}<span class=\\"nt\\">&lt;/li&gt;</span>\\n {{ endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span> \\n</code></pre></div></div>\\n\\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">my_custom_variable</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Hello</span><span class=\\"nv\\"> </span><span class=\\"s\\">World\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后就可以在模板文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\\n\\n<h4 id=\\"一site变量\\">一、site变量</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\\n\\n<h5 id=\\"1sitecategories\\">1、<code class=\\"language-plaintext highlighter-rouge\\">site.categories</code></h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\\"language-plaintext highlighter-rouge\\">first</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">category</code> 的名字,如下使用:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2sitepages\\">2、site.pages</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3其他常用属性\\">3、其他常用属性</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.title</code>:是网站的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.related_posts</code>:相关文章的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.data</code>:从 <code class=\\"language-plaintext highlighter-rouge\\">_data</code> 目录加载的数据。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.static_files</code>:静态文件的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.collections</code>:自定义集合的列表。</li>\\n</ul>\\n\\n<h4 id=\\"二page-变量\\">二、<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量</h4>\\n\\n<p>在 Jekyll 中,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量属性包括:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">layout</code>:表示页面使用的布局模板的名称。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">title</code>:表示页面的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">date</code>:表示页面的发布日期。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">categories</code>:表示页面所属的分类列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:表示页面所属的标签列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\\n</ul>\\n\\n<p>在模板中,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.属性名 }}</code> 的方式来访问 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.title }}</code>。此外,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量还有其他属性,如 <code class=\\"language-plaintext highlighter-rouge\\">permalink</code>、<code class=\\"language-plaintext highlighter-rouge\\">excerpt</code>、<code class=\\"language-plaintext highlighter-rouge\\">url</code> 等,可以根据需要调用。</p>\\n\\n<h3 id=\\"part-3控制结构\\">Part 3、控制结构</h3>\\n\\n<h4 id=\\"1if-else-分支结构\\">1、<code class=\\"language-plaintext highlighter-rouge\\">if-else</code> 分支结构</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ if tmp_var == \\"type1\\" %}\\n{{ elsif tmp_var == \\"type2\\" %}\\n{{ elsif tmp_var == \\"type3\\" %}\\n{{ elsif tmp_var == \\"type4\\" %}\\n{{ else tmp_var == \\"type5\\" %}\\n{{ endif %}\\n</code></pre></div></div>\\n\\n<h4 id=\\"2for-endfor-循环结构\\">2、<code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 循环结构</h4>\\n\\n<p>不带条件判断的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 循环如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for post in paginator.posts %}\\n\\t<span class=\\"c\\">&lt;!-- Your other sentences --&gt;</span>\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<p>带条件循环的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages | where: \\"dir\\", \\"categories\\" %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3jekyll-支持的其他结构包括\\">3、Jekyll 支持的其他结构包括:</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">capture</code> 用于捕获输出的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">cycle</code> 用于循环一组字符串的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">include</code> 用于包含其他文件的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">while</code> 用于在满足指定条件时执行代码的结构。</li>\\n</ul>\\n\\n<h3 id=\\"参考\\">参考:</h3>\\n\\n<p>1、<a href=\\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\\n2、<a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"AI":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\\n \\t<meta name=\\"description\\" content=\\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-24T15:08:01+00:00\\" class=\\"by-line\\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一自然语言处理领域近年的发展关键节点\\" id=\\"markdown-toc-一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</a> <ul>\\n <li><a href=\\"#1从理性主义到经验主义\\" id=\\"markdown-toc-1从理性主义到经验主义\\">1、从理性主义到经验主义</a></li>\\n <li><a href=\\"#2经验主义的早期还不是深度学习\\" id=\\"markdown-toc-2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</a></li>\\n <li><a href=\\"#3撇开特征让机器囫囵吞枣地学吧\\" id=\\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\\n <li><a href=\\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\" id=\\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\\n <li><a href=\\"#5自监督学习法让我们省去人工标注\\" id=\\"markdown-toc-5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</a></li>\\n <li><a href=\\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\\" id=\\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\\n <li><a href=\\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\" id=\\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\\n <li><a href=\\"#8数据和算力有了还不够\\" id=\\"markdown-toc-8数据和算力有了还不够\\">8、数据和算力有了,还不够</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二学术里程碑几篇重量级论文\\" id=\\"markdown-toc-二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</a> <ul>\\n <li><a href=\\"#1提出-transformer-的attention-is-all-you-need2017\\" id=\\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\\n <li><a href=\\"#2elmo-deep-contextualized-word-representations\\" id=\\"markdown-toc-2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</a></li>\\n <li><a href=\\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\" id=\\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\\n <li><a href=\\"#4gpt-3-language-models-are-few-shot-learners2020\\" id=\\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\\n <li><a href=\\"#其他的重量级论文\\" id=\\"markdown-toc-其他的重量级论文\\">其他的重量级论文</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三行业里程碑\\" id=\\"markdown-toc-三行业里程碑\\">三、行业里程碑</a></li>\\n <li><a href=\\"#四成本\\" id=\\"markdown-toc-四成本\\">四、成本</a></li>\\n <li><a href=\\"#五业内应用\\" id=\\"markdown-toc-五业内应用\\">五、业内应用</a></li>\\n <li><a href=\\"#五行业内哪些人的言论值得我们日常重点关注\\" id=\\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</h3>\\n\\n<p><img src=\\"/img/src/2022-12-17-ai-bert-1-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h4 id=\\"1从理性主义到经验主义\\">1、从理性主义到经验主义</h4>\\n\\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\\n\\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\\n\\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\\n\\n<h4 id=\\"2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</h4>\\n\\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\\n\\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\\n\\n<h4 id=\\"3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\\n\\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\\n\\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\\n\\n<h4 id=\\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\\n\\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\\n\\n<h4 id=\\"5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</h4>\\n\\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\\n\\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\\n\\n<h4 id=\\"6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\\n\\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\\n\\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\\n\\n<h4 id=\\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\\n\\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\\n\\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\\n\\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\\n\\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\\n\\n<h4 id=\\"8数据和算力有了还不够\\">8、数据和算力有了,还不够</h4>\\n\\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\\n\\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\\n\\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\\n\\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\\n\\n<h3 id=\\"二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</h3>\\n\\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\\n\\n<h4 id=\\"1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\\n\\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\\n\\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\\n\\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\\n\\n<h4 id=\\"2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</h4>\\n\\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\\n\\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\\n\\n<h4 id=\\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\\n\\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\\n\\n<ul>\\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\\n <li>在预训练中引入自监督学习任务。</li>\\n</ul>\\n\\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\\n\\n<h4 id=\\"4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\\n\\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\\n\\n<h4 id=\\"其他的重量级论文\\">其他的重量级论文</h4>\\n\\n<ul>\\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\\n <li>……</li>\\n</ul>\\n\\n<h3 id=\\"三行业里程碑\\">三、行业里程碑</h3>\\n\\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\\n\\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\\n\\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\\n\\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\\n\\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\\n\\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\\n\\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\\n\\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\\n\\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\\n\\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\\n\\n<h3 id=\\"四成本\\">四、成本</h3>\\n\\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\\n\\n<ul>\\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\\n</ul>\\n\\n<h3 id=\\"五业内应用\\">五、业内应用</h3>\\n\\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\\n\\n<ul>\\n <li>虚拟客服(可以乱真的)</li>\\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\\n <li>智能翻译</li>\\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\\n <li>AI 广告公司:替代传统广告公司</li>\\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\\n</ul>\\n\\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\\n\\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\\n\\n<ul>\\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-7.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-2.jpg\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-8.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年年底,西湖心辰公司发布「<a href=\\"https://www.heyfriday.cn/\\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-1.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</h3>\\n\\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\\n\\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\\n\\n<blockquote>\\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\\n</blockquote>\\n\\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\\n\\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\\n\\n<blockquote>\\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\\n</blockquote>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://beta.openai.com/docs/models</li>\\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\\n <li>https://hub.baai.ac.cn/view/21726</li>\\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\\n <li>https://www.sohu.com/a/615541698_121255906</li>\\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\\n \\t<meta name=\\"description\\" content=\\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-17T15:08:01+00:00\\" class=\\"by-line\\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一关于-bert-的一些背景\\" id=\\"markdown-toc-一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</a></li>\\n <li><a href=\\"#二开始一个-bert-的动手小试验\\" id=\\"markdown-toc-二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</a> <ul>\\n <li><a href=\\"#1安装-anaconda-来为部署-bert-做环境准备\\" id=\\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\\n <li><a href=\\"#2安装-bert-所需要的各种依赖\\" id=\\"markdown-toc-2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</a></li>\\n <li><a href=\\"#3下载一个预训练pre-train过的-bert-模型\\" id=\\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\\n <li><a href=\\"#5启动-bert-服务端\\" id=\\"markdown-toc-5启动-bert-服务端\\">5、启动 BERT 服务端</a></li>\\n <li><a href=\\"#6在-pycharm-中使用-conda-的环境\\" id=\\"markdown-toc-6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</a></li>\\n <li><a href=\\"#7编写程序实现-bert-客户端\\" id=\\"markdown-toc-7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三bert-模型的优劣势及其原因\\" id=\\"markdown-toc-三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</a> <ul>\\n <li><a href=\\"#1bert-的优势是很明显的\\" id=\\"markdown-toc-1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</a> <ul>\\n <li><a href=\\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\" id=\\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\\n <li><a href=\\"#12识别并专注于较重要的部分进行文本处理\\" id=\\"markdown-toc-12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\\n <li><a href=\\"#13快速构建针对具体任务的-nlp-系统\\" id=\\"markdown-toc-13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2bert-模型的劣势及其原因\\" id=\\"markdown-toc-2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</a> <ul>\\n <li><a href=\\"#21随机挖-mask-的完形填空题是有隐患的\\" id=\\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\\n <li><a href=\\"#22nsp-任务有必要吗\\" id=\\"markdown-toc-22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</a></li>\\n <li><a href=\\"#23针对两个或以上词组成的连续词的词义被丢失\\" id=\\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\\n <li><a href=\\"#24需要的算力高\\" id=\\"markdown-toc-24需要的算力高\\">2.4、需要的算力高</a></li>\\n <li><a href=\\"#25需要的模型大\\" id=\\"markdown-toc-25需要的模型大\\">2.5、需要的模型大</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四一些关于-bert-的问题\\" id=\\"markdown-toc-四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</a> <ul>\\n <li><a href=\\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\\" id=\\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\\n <li><a href=\\"#2为什么-bert-可以比-rnn-更好地并行化\\" id=\\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</h3>\\n\\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\\n\\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\\n\\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\\n\\n<h3 id=\\"二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</h3>\\n\\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\\n\\n<h4 id=\\"1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\\n\\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\\n\\n<p>更新 conda 到最新版本:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda update <span class=\\"nt\\">-n</span> base conda\\n</code></pre></div></div>\\n\\n<p>使用 Python 3.7 创建一个新的环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda create <span class=\\"nt\\">-n</span> py37 <span class=\\"nv\\">python</span><span class=\\"o\\">=</span>3.7\\n</code></pre></div></div>\\n\\n<p>激活这个新环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda activate py37\\n</code></pre></div></div>\\n\\n<p>验证正在使用的是正确版本的 Python</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>python <span class=\\"nt\\">--version</span>\\n</code></pre></div></div>\\n\\n<p>另外你可能还会用到的 conda 命令有:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\\nconda deactivate py37\\n\\n<span class=\\"c\\"># 查看 conda 当前安装的所有库</span>\\nconda list\\n</code></pre></div></div>\\n\\n<h4 id=\\"2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda <span class=\\"nb\\">install </span><span class=\\"nv\\">tensorflow</span><span class=\\"o\\">==</span>1.14.0\\n</code></pre></div></div>\\n\\n<p>验证 tensorflow 是否安装正确:</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">import</span> <span class=\\"nn\\">tensorflow</span> <span class=\\"k\\">as</span> <span class=\\"n\\">tf</span>\\n<span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"n\\">tf</span><span class=\\"p\\">.</span><span class=\\"n\\">__version__</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\\n\\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\\n\\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\\n\\n<ul>\\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n</ul>\\n\\n<p>4、安装 BERT 的服务端和客户端</p>\\n\\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\\n\\n<p>在你激活的环境里,安装 <code class=\\"language-plaintext highlighter-rouge\\">bert-as-service</code>:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 安装服务端和客户端</span>\\n<span class=\\"c\\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\\nconda <span class=\\"nb\\">install </span>bert-serving-server bert-serving-client \\n验证 bert-as-service 是否安装成功\\nbert-serving-start <span class=\\"nt\\">-h</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5启动-bert-服务端\\">5、启动 BERT 服务端</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 命令行下启动BERT服务</span>\\n<span class=\\"c\\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\\nbert-serving-start <span class=\\"nt\\">-model_dir</span> /模型/的/绝对/路径 <span class=\\"nt\\">-num_worker</span><span class=\\"o\\">=</span>4\\n</code></pre></div></div>\\n\\n<h4 id=\\"6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</h4>\\n\\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\\n\\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\\"language-plaintext highlighter-rouge\\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\\n\\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\\n\\n<h4 id=\\"7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</h4>\\n\\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">from</span> <span class=\\"nn\\">bert_serving.client</span> <span class=\\"kn\\">import</span> <span class=\\"n\\">BertClient</span>\\n<span class=\\"kn\\">import</span> <span class=\\"nn\\">numpy</span> <span class=\\"k\\">as</span> <span class=\\"n\\">np</span>\\n\\n<span class=\\"c1\\"># 定义类\\n</span><span class=\\"k\\">class</span> <span class=\\"nc\\">BertModel</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">__init__</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"k\\">try</span><span class=\\"p\\">:</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertClient</span><span class=\\"p\\">(</span><span class=\\"n\\">ip</span><span class=\\"o\\">=</span><span class=\\"s\\">'127.0.0.1'</span><span class=\\"p\\">,</span> <span class=\\"n\\">port</span><span class=\\"o\\">=</span><span class=\\"mi\\">5555</span><span class=\\"p\\">,</span> <span class=\\"n\\">port_out</span><span class=\\"o\\">=</span><span class=\\"mi\\">5556</span><span class=\\"p\\">)</span> <span class=\\"c1\\"># 创建客户端对象\\n</span> <span class=\\"c1\\"># 注意:可以参考API,查看其它参数的设置\\n</span> <span class=\\"c1\\"># 127.0.0.1 表示本机IP,也可以用localhost\\n</span> <span class=\\"k\\">except</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">raise</span> <span class=\\"nb\\">Exception</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cannot create BertClient\\"</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">close_bert</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">text</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''对输入文本进行embedding\\n Args:\\n text: str, 输入文本\\n Returns:\\n text_vector: float, 返回一个列表,包含text的embedding编码值\\n '''</span>\\n <span class=\\"n\\">text_vector</span> <span class=\\"o\\">=</span> <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">encode</span><span class=\\"p\\">([</span><span class=\\"n\\">text</span><span class=\\"p\\">])[</span><span class=\\"mi\\">0</span><span class=\\"p\\">]</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">text_vector</span> <span class=\\"c1\\"># 获取输出结果\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_1</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_2</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''根据两个语句的vector,计算它们的相似性\\n Args:\\n vec_1: float, 语句1的vector\\n vec_2: float, 语句2的vector\\n Returns:\\n sim_value: float, 返回相似性的计算值\\n '''</span>\\n <span class=\\"c1\\"># 根据cosine的计算公式\\n</span> <span class=\\"n\\">v1</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_1</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">v2</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">float</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span> <span class=\\"o\\">*</span> <span class=\\"n\\">v2</span><span class=\\"p\\">.</span><span class=\\"n\\">T</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span><span class=\\"p\\">)</span> <span class=\\"o\\">*</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">cosine</span> <span class=\\"o\\">=</span> <span class=\\"n\\">a</span> <span class=\\"o\\">/</span> <span class=\\"n\\">b</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">cosine</span>\\n\\n\\n<span class=\\"k\\">if</span> <span class=\\"n\\">__name__</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"__main__\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># 创建bert对象\\n</span> <span class=\\"n\\">bert</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertModel</span><span class=\\"p\\">()</span>\\n <span class=\\"k\\">while</span> <span class=\\"bp\\">True</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># --- 输入语句 ----\\n</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句1: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">if</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"N\\"</span> <span class=\\"ow\\">or</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"n\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">close_bert</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span> <span class=\\"k\\">break</span>\\n\\n <span class=\\"n\\">input_b</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句2: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># --- 对输入语句进行embedding ---\\n</span>\\n <span class=\\"n\\">a_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_a</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'a_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">a_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"n\\">b_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_b</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'b_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 计算两个语句的相似性\\n</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"n\\">a_vec</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'cosine value : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">cos</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'</span><span class=\\"se\\">\\\\n\\\\n</span><span class=\\"s\\">'</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\\n</span> <span class=\\"k\\">if</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">&gt;</span> <span class=\\"mf\\">0.85</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"2个语句的含义相似\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">else</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"不相似\\"</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<p>在使用 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 连接 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 时,你需要确保 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 使用的模型和 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\\n\\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>请输入语句1: \\n请输入语句2: \\n</code></pre></div></div>\\n\\n<p>两句输入好确认后,得到如下形式的结果:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>a_vec shape : (768,)\\nb_vec shape : (768,)\\ncosine value : 0.8691698561422959\\n</code></pre></div></div>\\n\\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\\n\\n<h3 id=\\"三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</h3>\\n\\n<p>论文地址:<a href=\\"https://arxiv.org/abs/1810.04805\\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\\n\\n<h4 id=\\"1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</h4>\\n\\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\\n\\n<blockquote>\\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\\n</blockquote>\\n\\n<h5 id=\\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\\n\\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\\n\\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\\n\\n<h5 id=\\"12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</h5>\\n\\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\\n\\n<h5 id=\\"13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</h5>\\n\\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\\n\\n<h4 id=\\"2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</h4>\\n\\n<h5 id=\\"21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\\n\\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\\n\\n<h5 id=\\"22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</h5>\\n\\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\\n\\n<h5 id=\\"23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\\n\\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\\n\\n<h5 id=\\"24需要的算力高\\">2.4、需要的算力高</h5>\\n\\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\\n\\n<h5 id=\\"25需要的模型大\\">2.5、需要的模型大</h5>\\n\\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\\n\\n<h3 id=\\"四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</h3>\\n\\n<h4 id=\\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\\n\\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\\n\\n<h4 id=\\"2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\\n\\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://arxiv.org/abs/1810.04805</li>\\n <li>https://github.com/google-research/bert</li>\\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\\n \\t<meta name=\\"description\\" content=\\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-11T15:59:57+00:00\\" class=\\"by-line\\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2022-12-11-wechat-chatgpt-3.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\\n\\n<h3 id=\\"stepbystep\\">Step by step</h3>\\n\\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\\n\\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\\n\\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\\n\\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\\n\\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\\n\\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\\n\\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">chatGPTAccountPool</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"na\\">email</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your email&gt;</span>\\n <span class=\\"na\\">password</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your password&gt;</span>\\n<span class=\\"c1\\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\\n<span class=\\"na\\">chatPrivateTiggerKeyword</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker pull holegots/wechat-chatgpt:latest。\\n</code></pre></div></div>\\n\\n<p>第八步,启动 wechat-chatgpt:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker run <span class=\\"nt\\">-d</span> <span class=\\"nt\\">--name</span> wechat-chatgpt <span class=\\"nt\\">-v</span> <span class=\\"si\\">$(</span><span class=\\"nb\\">pwd</span><span class=\\"si\\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\\n</code></pre></div></div>\\n\\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\\"language-plaintext highlighter-rouge\\">npm install &amp;&amp; poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-1.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-2.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\\n\\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker logs <span class=\\"nt\\">-f</span> wechat-chatgpt\\n</code></pre></div></div>\\n\\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\\n\\n<p>如果要停止运行,用如下命令:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker stop wechat-chatgpt\\n</code></pre></div></div>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<p>1、<a href=\\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"人工智能":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\\n \\t<meta name=\\"description\\" content=\\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-24T15:08:01+00:00\\" class=\\"by-line\\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一自然语言处理领域近年的发展关键节点\\" id=\\"markdown-toc-一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</a> <ul>\\n <li><a href=\\"#1从理性主义到经验主义\\" id=\\"markdown-toc-1从理性主义到经验主义\\">1、从理性主义到经验主义</a></li>\\n <li><a href=\\"#2经验主义的早期还不是深度学习\\" id=\\"markdown-toc-2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</a></li>\\n <li><a href=\\"#3撇开特征让机器囫囵吞枣地学吧\\" id=\\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\\n <li><a href=\\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\" id=\\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\\n <li><a href=\\"#5自监督学习法让我们省去人工标注\\" id=\\"markdown-toc-5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</a></li>\\n <li><a href=\\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\\" id=\\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\\n <li><a href=\\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\" id=\\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\\n <li><a href=\\"#8数据和算力有了还不够\\" id=\\"markdown-toc-8数据和算力有了还不够\\">8、数据和算力有了,还不够</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二学术里程碑几篇重量级论文\\" id=\\"markdown-toc-二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</a> <ul>\\n <li><a href=\\"#1提出-transformer-的attention-is-all-you-need2017\\" id=\\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\\n <li><a href=\\"#2elmo-deep-contextualized-word-representations\\" id=\\"markdown-toc-2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</a></li>\\n <li><a href=\\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\" id=\\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\\n <li><a href=\\"#4gpt-3-language-models-are-few-shot-learners2020\\" id=\\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\\n <li><a href=\\"#其他的重量级论文\\" id=\\"markdown-toc-其他的重量级论文\\">其他的重量级论文</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三行业里程碑\\" id=\\"markdown-toc-三行业里程碑\\">三、行业里程碑</a></li>\\n <li><a href=\\"#四成本\\" id=\\"markdown-toc-四成本\\">四、成本</a></li>\\n <li><a href=\\"#五业内应用\\" id=\\"markdown-toc-五业内应用\\">五、业内应用</a></li>\\n <li><a href=\\"#五行业内哪些人的言论值得我们日常重点关注\\" id=\\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</h3>\\n\\n<p><img src=\\"/img/src/2022-12-17-ai-bert-1-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h4 id=\\"1从理性主义到经验主义\\">1、从理性主义到经验主义</h4>\\n\\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\\n\\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\\n\\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\\n\\n<h4 id=\\"2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</h4>\\n\\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\\n\\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\\n\\n<h4 id=\\"3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\\n\\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\\n\\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\\n\\n<h4 id=\\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\\n\\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\\n\\n<h4 id=\\"5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</h4>\\n\\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\\n\\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\\n\\n<h4 id=\\"6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\\n\\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\\n\\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\\n\\n<h4 id=\\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\\n\\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\\n\\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\\n\\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\\n\\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\\n\\n<h4 id=\\"8数据和算力有了还不够\\">8、数据和算力有了,还不够</h4>\\n\\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\\n\\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\\n\\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\\n\\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\\n\\n<h3 id=\\"二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</h3>\\n\\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\\n\\n<h4 id=\\"1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\\n\\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\\n\\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\\n\\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\\n\\n<h4 id=\\"2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</h4>\\n\\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\\n\\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\\n\\n<h4 id=\\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\\n\\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\\n\\n<ul>\\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\\n <li>在预训练中引入自监督学习任务。</li>\\n</ul>\\n\\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\\n\\n<h4 id=\\"4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\\n\\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\\n\\n<h4 id=\\"其他的重量级论文\\">其他的重量级论文</h4>\\n\\n<ul>\\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\\n <li>……</li>\\n</ul>\\n\\n<h3 id=\\"三行业里程碑\\">三、行业里程碑</h3>\\n\\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\\n\\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\\n\\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\\n\\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\\n\\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\\n\\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\\n\\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\\n\\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\\n\\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\\n\\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\\n\\n<h3 id=\\"四成本\\">四、成本</h3>\\n\\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\\n\\n<ul>\\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\\n</ul>\\n\\n<h3 id=\\"五业内应用\\">五、业内应用</h3>\\n\\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\\n\\n<ul>\\n <li>虚拟客服(可以乱真的)</li>\\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\\n <li>智能翻译</li>\\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\\n <li>AI 广告公司:替代传统广告公司</li>\\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\\n</ul>\\n\\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\\n\\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\\n\\n<ul>\\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-7.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-2.jpg\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-8.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年年底,西湖心辰公司发布「<a href=\\"https://www.heyfriday.cn/\\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-1.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</h3>\\n\\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\\n\\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\\n\\n<blockquote>\\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\\n</blockquote>\\n\\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\\n\\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\\n\\n<blockquote>\\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\\n</blockquote>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://beta.openai.com/docs/models</li>\\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\\n <li>https://hub.baai.ac.cn/view/21726</li>\\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\\n <li>https://www.sohu.com/a/615541698_121255906</li>\\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\\n \\t<meta name=\\"description\\" content=\\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-17T15:08:01+00:00\\" class=\\"by-line\\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一关于-bert-的一些背景\\" id=\\"markdown-toc-一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</a></li>\\n <li><a href=\\"#二开始一个-bert-的动手小试验\\" id=\\"markdown-toc-二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</a> <ul>\\n <li><a href=\\"#1安装-anaconda-来为部署-bert-做环境准备\\" id=\\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\\n <li><a href=\\"#2安装-bert-所需要的各种依赖\\" id=\\"markdown-toc-2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</a></li>\\n <li><a href=\\"#3下载一个预训练pre-train过的-bert-模型\\" id=\\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\\n <li><a href=\\"#5启动-bert-服务端\\" id=\\"markdown-toc-5启动-bert-服务端\\">5、启动 BERT 服务端</a></li>\\n <li><a href=\\"#6在-pycharm-中使用-conda-的环境\\" id=\\"markdown-toc-6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</a></li>\\n <li><a href=\\"#7编写程序实现-bert-客户端\\" id=\\"markdown-toc-7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三bert-模型的优劣势及其原因\\" id=\\"markdown-toc-三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</a> <ul>\\n <li><a href=\\"#1bert-的优势是很明显的\\" id=\\"markdown-toc-1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</a> <ul>\\n <li><a href=\\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\" id=\\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\\n <li><a href=\\"#12识别并专注于较重要的部分进行文本处理\\" id=\\"markdown-toc-12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\\n <li><a href=\\"#13快速构建针对具体任务的-nlp-系统\\" id=\\"markdown-toc-13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2bert-模型的劣势及其原因\\" id=\\"markdown-toc-2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</a> <ul>\\n <li><a href=\\"#21随机挖-mask-的完形填空题是有隐患的\\" id=\\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\\n <li><a href=\\"#22nsp-任务有必要吗\\" id=\\"markdown-toc-22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</a></li>\\n <li><a href=\\"#23针对两个或以上词组成的连续词的词义被丢失\\" id=\\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\\n <li><a href=\\"#24需要的算力高\\" id=\\"markdown-toc-24需要的算力高\\">2.4、需要的算力高</a></li>\\n <li><a href=\\"#25需要的模型大\\" id=\\"markdown-toc-25需要的模型大\\">2.5、需要的模型大</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四一些关于-bert-的问题\\" id=\\"markdown-toc-四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</a> <ul>\\n <li><a href=\\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\\" id=\\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\\n <li><a href=\\"#2为什么-bert-可以比-rnn-更好地并行化\\" id=\\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</h3>\\n\\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\\n\\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\\n\\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\\n\\n<h3 id=\\"二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</h3>\\n\\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\\n\\n<h4 id=\\"1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\\n\\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\\n\\n<p>更新 conda 到最新版本:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda update <span class=\\"nt\\">-n</span> base conda\\n</code></pre></div></div>\\n\\n<p>使用 Python 3.7 创建一个新的环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda create <span class=\\"nt\\">-n</span> py37 <span class=\\"nv\\">python</span><span class=\\"o\\">=</span>3.7\\n</code></pre></div></div>\\n\\n<p>激活这个新环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda activate py37\\n</code></pre></div></div>\\n\\n<p>验证正在使用的是正确版本的 Python</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>python <span class=\\"nt\\">--version</span>\\n</code></pre></div></div>\\n\\n<p>另外你可能还会用到的 conda 命令有:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\\nconda deactivate py37\\n\\n<span class=\\"c\\"># 查看 conda 当前安装的所有库</span>\\nconda list\\n</code></pre></div></div>\\n\\n<h4 id=\\"2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda <span class=\\"nb\\">install </span><span class=\\"nv\\">tensorflow</span><span class=\\"o\\">==</span>1.14.0\\n</code></pre></div></div>\\n\\n<p>验证 tensorflow 是否安装正确:</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">import</span> <span class=\\"nn\\">tensorflow</span> <span class=\\"k\\">as</span> <span class=\\"n\\">tf</span>\\n<span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"n\\">tf</span><span class=\\"p\\">.</span><span class=\\"n\\">__version__</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\\n\\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\\n\\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\\n\\n<ul>\\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n</ul>\\n\\n<p>4、安装 BERT 的服务端和客户端</p>\\n\\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\\n\\n<p>在你激活的环境里,安装 <code class=\\"language-plaintext highlighter-rouge\\">bert-as-service</code>:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 安装服务端和客户端</span>\\n<span class=\\"c\\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\\nconda <span class=\\"nb\\">install </span>bert-serving-server bert-serving-client \\n验证 bert-as-service 是否安装成功\\nbert-serving-start <span class=\\"nt\\">-h</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5启动-bert-服务端\\">5、启动 BERT 服务端</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 命令行下启动BERT服务</span>\\n<span class=\\"c\\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\\nbert-serving-start <span class=\\"nt\\">-model_dir</span> /模型/的/绝对/路径 <span class=\\"nt\\">-num_worker</span><span class=\\"o\\">=</span>4\\n</code></pre></div></div>\\n\\n<h4 id=\\"6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</h4>\\n\\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\\n\\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\\"language-plaintext highlighter-rouge\\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\\n\\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\\n\\n<h4 id=\\"7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</h4>\\n\\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">from</span> <span class=\\"nn\\">bert_serving.client</span> <span class=\\"kn\\">import</span> <span class=\\"n\\">BertClient</span>\\n<span class=\\"kn\\">import</span> <span class=\\"nn\\">numpy</span> <span class=\\"k\\">as</span> <span class=\\"n\\">np</span>\\n\\n<span class=\\"c1\\"># 定义类\\n</span><span class=\\"k\\">class</span> <span class=\\"nc\\">BertModel</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">__init__</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"k\\">try</span><span class=\\"p\\">:</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertClient</span><span class=\\"p\\">(</span><span class=\\"n\\">ip</span><span class=\\"o\\">=</span><span class=\\"s\\">'127.0.0.1'</span><span class=\\"p\\">,</span> <span class=\\"n\\">port</span><span class=\\"o\\">=</span><span class=\\"mi\\">5555</span><span class=\\"p\\">,</span> <span class=\\"n\\">port_out</span><span class=\\"o\\">=</span><span class=\\"mi\\">5556</span><span class=\\"p\\">)</span> <span class=\\"c1\\"># 创建客户端对象\\n</span> <span class=\\"c1\\"># 注意:可以参考API,查看其它参数的设置\\n</span> <span class=\\"c1\\"># 127.0.0.1 表示本机IP,也可以用localhost\\n</span> <span class=\\"k\\">except</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">raise</span> <span class=\\"nb\\">Exception</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cannot create BertClient\\"</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">close_bert</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">text</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''对输入文本进行embedding\\n Args:\\n text: str, 输入文本\\n Returns:\\n text_vector: float, 返回一个列表,包含text的embedding编码值\\n '''</span>\\n <span class=\\"n\\">text_vector</span> <span class=\\"o\\">=</span> <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">encode</span><span class=\\"p\\">([</span><span class=\\"n\\">text</span><span class=\\"p\\">])[</span><span class=\\"mi\\">0</span><span class=\\"p\\">]</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">text_vector</span> <span class=\\"c1\\"># 获取输出结果\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_1</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_2</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''根据两个语句的vector,计算它们的相似性\\n Args:\\n vec_1: float, 语句1的vector\\n vec_2: float, 语句2的vector\\n Returns:\\n sim_value: float, 返回相似性的计算值\\n '''</span>\\n <span class=\\"c1\\"># 根据cosine的计算公式\\n</span> <span class=\\"n\\">v1</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_1</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">v2</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">float</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span> <span class=\\"o\\">*</span> <span class=\\"n\\">v2</span><span class=\\"p\\">.</span><span class=\\"n\\">T</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span><span class=\\"p\\">)</span> <span class=\\"o\\">*</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">cosine</span> <span class=\\"o\\">=</span> <span class=\\"n\\">a</span> <span class=\\"o\\">/</span> <span class=\\"n\\">b</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">cosine</span>\\n\\n\\n<span class=\\"k\\">if</span> <span class=\\"n\\">__name__</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"__main__\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># 创建bert对象\\n</span> <span class=\\"n\\">bert</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertModel</span><span class=\\"p\\">()</span>\\n <span class=\\"k\\">while</span> <span class=\\"bp\\">True</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># --- 输入语句 ----\\n</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句1: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">if</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"N\\"</span> <span class=\\"ow\\">or</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"n\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">close_bert</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span> <span class=\\"k\\">break</span>\\n\\n <span class=\\"n\\">input_b</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句2: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># --- 对输入语句进行embedding ---\\n</span>\\n <span class=\\"n\\">a_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_a</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'a_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">a_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"n\\">b_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_b</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'b_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 计算两个语句的相似性\\n</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"n\\">a_vec</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'cosine value : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">cos</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'</span><span class=\\"se\\">\\\\n\\\\n</span><span class=\\"s\\">'</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\\n</span> <span class=\\"k\\">if</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">&gt;</span> <span class=\\"mf\\">0.85</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"2个语句的含义相似\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">else</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"不相似\\"</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<p>在使用 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 连接 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 时,你需要确保 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 使用的模型和 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\\n\\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>请输入语句1: \\n请输入语句2: \\n</code></pre></div></div>\\n\\n<p>两句输入好确认后,得到如下形式的结果:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>a_vec shape : (768,)\\nb_vec shape : (768,)\\ncosine value : 0.8691698561422959\\n</code></pre></div></div>\\n\\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\\n\\n<h3 id=\\"三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</h3>\\n\\n<p>论文地址:<a href=\\"https://arxiv.org/abs/1810.04805\\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\\n\\n<h4 id=\\"1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</h4>\\n\\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\\n\\n<blockquote>\\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\\n</blockquote>\\n\\n<h5 id=\\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\\n\\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\\n\\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\\n\\n<h5 id=\\"12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</h5>\\n\\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\\n\\n<h5 id=\\"13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</h5>\\n\\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\\n\\n<h4 id=\\"2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</h4>\\n\\n<h5 id=\\"21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\\n\\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\\n\\n<h5 id=\\"22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</h5>\\n\\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\\n\\n<h5 id=\\"23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\\n\\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\\n\\n<h5 id=\\"24需要的算力高\\">2.4、需要的算力高</h5>\\n\\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\\n\\n<h5 id=\\"25需要的模型大\\">2.5、需要的模型大</h5>\\n\\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\\n\\n<h3 id=\\"四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</h3>\\n\\n<h4 id=\\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\\n\\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\\n\\n<h4 id=\\"2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\\n\\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://arxiv.org/abs/1810.04805</li>\\n <li>https://github.com/google-research/bert</li>\\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\\n \\t<meta name=\\"description\\" content=\\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-11T15:59:57+00:00\\" class=\\"by-line\\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2022-12-11-wechat-chatgpt-3.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\\n\\n<h3 id=\\"stepbystep\\">Step by step</h3>\\n\\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\\n\\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\\n\\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\\n\\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\\n\\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\\n\\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\\n\\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">chatGPTAccountPool</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"na\\">email</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your email&gt;</span>\\n <span class=\\"na\\">password</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your password&gt;</span>\\n<span class=\\"c1\\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\\n<span class=\\"na\\">chatPrivateTiggerKeyword</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker pull holegots/wechat-chatgpt:latest。\\n</code></pre></div></div>\\n\\n<p>第八步,启动 wechat-chatgpt:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker run <span class=\\"nt\\">-d</span> <span class=\\"nt\\">--name</span> wechat-chatgpt <span class=\\"nt\\">-v</span> <span class=\\"si\\">$(</span><span class=\\"nb\\">pwd</span><span class=\\"si\\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\\n</code></pre></div></div>\\n\\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\\"language-plaintext highlighter-rouge\\">npm install &amp;&amp; poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-1.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-2.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\\n\\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker logs <span class=\\"nt\\">-f</span> wechat-chatgpt\\n</code></pre></div></div>\\n\\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\\n\\n<p>如果要停止运行,用如下命令:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker stop wechat-chatgpt\\n</code></pre></div></div>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<p>1、<a href=\\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"diffusion":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"MidJourney":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"Text2Image":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"文生图":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"AIGC":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"ChatGPT":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\\n \\t<meta name=\\"description\\" content=\\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-11T15:59:57+00:00\\" class=\\"by-line\\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2022-12-11-wechat-chatgpt-3.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\\n\\n<h3 id=\\"stepbystep\\">Step by step</h3>\\n\\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\\n\\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\\n\\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\\n\\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\\n\\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\\n\\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\\n\\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">chatGPTAccountPool</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"na\\">email</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your email&gt;</span>\\n <span class=\\"na\\">password</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your password&gt;</span>\\n<span class=\\"c1\\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\\n<span class=\\"na\\">chatPrivateTiggerKeyword</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker pull holegots/wechat-chatgpt:latest。\\n</code></pre></div></div>\\n\\n<p>第八步,启动 wechat-chatgpt:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker run <span class=\\"nt\\">-d</span> <span class=\\"nt\\">--name</span> wechat-chatgpt <span class=\\"nt\\">-v</span> <span class=\\"si\\">$(</span><span class=\\"nb\\">pwd</span><span class=\\"si\\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\\n</code></pre></div></div>\\n\\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\\"language-plaintext highlighter-rouge\\">npm install &amp;&amp; poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-1.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-2.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\\n\\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker logs <span class=\\"nt\\">-f</span> wechat-chatgpt\\n</code></pre></div></div>\\n\\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\\n\\n<p>如果要停止运行,用如下命令:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker stop wechat-chatgpt\\n</code></pre></div></div>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<p>1、<a href=\\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"OpenAI":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\\n \\t<meta name=\\"description\\" content=\\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-11T15:59:57+00:00\\" class=\\"by-line\\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2022-12-11-wechat-chatgpt-3.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\\n\\n<h3 id=\\"stepbystep\\">Step by step</h3>\\n\\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\\n\\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\\n\\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\\n\\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\\n\\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\\n\\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\\n\\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">chatGPTAccountPool</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"na\\">email</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your email&gt;</span>\\n <span class=\\"na\\">password</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your password&gt;</span>\\n<span class=\\"c1\\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\\n<span class=\\"na\\">chatPrivateTiggerKeyword</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker pull holegots/wechat-chatgpt:latest。\\n</code></pre></div></div>\\n\\n<p>第八步,启动 wechat-chatgpt:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker run <span class=\\"nt\\">-d</span> <span class=\\"nt\\">--name</span> wechat-chatgpt <span class=\\"nt\\">-v</span> <span class=\\"si\\">$(</span><span class=\\"nb\\">pwd</span><span class=\\"si\\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\\n</code></pre></div></div>\\n\\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\\"language-plaintext highlighter-rouge\\">npm install &amp;&amp; poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-1.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-2.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\\n\\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker logs <span class=\\"nt\\">-f</span> wechat-chatgpt\\n</code></pre></div></div>\\n\\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\\n\\n<p>如果要停止运行,用如下命令:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker stop wechat-chatgpt\\n</code></pre></div></div>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<p>1、<a href=\\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"微信":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\\n \\t<meta name=\\"description\\" content=\\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-11T15:59:57+00:00\\" class=\\"by-line\\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2022-12-11-wechat-chatgpt-3.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\\n\\n<h3 id=\\"stepbystep\\">Step by step</h3>\\n\\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\\n\\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\\n\\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\\n\\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\\n\\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\\n\\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\\n\\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">chatGPTAccountPool</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"na\\">email</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your email&gt;</span>\\n <span class=\\"na\\">password</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your password&gt;</span>\\n<span class=\\"c1\\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\\n<span class=\\"na\\">chatPrivateTiggerKeyword</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker pull holegots/wechat-chatgpt:latest。\\n</code></pre></div></div>\\n\\n<p>第八步,启动 wechat-chatgpt:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker run <span class=\\"nt\\">-d</span> <span class=\\"nt\\">--name</span> wechat-chatgpt <span class=\\"nt\\">-v</span> <span class=\\"si\\">$(</span><span class=\\"nb\\">pwd</span><span class=\\"si\\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\\n</code></pre></div></div>\\n\\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\\"language-plaintext highlighter-rouge\\">npm install &amp;&amp; poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-1.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-2.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\\n\\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker logs <span class=\\"nt\\">-f</span> wechat-chatgpt\\n</code></pre></div></div>\\n\\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\\n\\n<p>如果要停止运行,用如下命令:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker stop wechat-chatgpt\\n</code></pre></div></div>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<p>1、<a href=\\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"BERT":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\\n \\t<meta name=\\"description\\" content=\\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-17T15:08:01+00:00\\" class=\\"by-line\\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一关于-bert-的一些背景\\" id=\\"markdown-toc-一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</a></li>\\n <li><a href=\\"#二开始一个-bert-的动手小试验\\" id=\\"markdown-toc-二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</a> <ul>\\n <li><a href=\\"#1安装-anaconda-来为部署-bert-做环境准备\\" id=\\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\\n <li><a href=\\"#2安装-bert-所需要的各种依赖\\" id=\\"markdown-toc-2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</a></li>\\n <li><a href=\\"#3下载一个预训练pre-train过的-bert-模型\\" id=\\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\\n <li><a href=\\"#5启动-bert-服务端\\" id=\\"markdown-toc-5启动-bert-服务端\\">5、启动 BERT 服务端</a></li>\\n <li><a href=\\"#6在-pycharm-中使用-conda-的环境\\" id=\\"markdown-toc-6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</a></li>\\n <li><a href=\\"#7编写程序实现-bert-客户端\\" id=\\"markdown-toc-7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三bert-模型的优劣势及其原因\\" id=\\"markdown-toc-三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</a> <ul>\\n <li><a href=\\"#1bert-的优势是很明显的\\" id=\\"markdown-toc-1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</a> <ul>\\n <li><a href=\\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\" id=\\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\\n <li><a href=\\"#12识别并专注于较重要的部分进行文本处理\\" id=\\"markdown-toc-12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\\n <li><a href=\\"#13快速构建针对具体任务的-nlp-系统\\" id=\\"markdown-toc-13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2bert-模型的劣势及其原因\\" id=\\"markdown-toc-2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</a> <ul>\\n <li><a href=\\"#21随机挖-mask-的完形填空题是有隐患的\\" id=\\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\\n <li><a href=\\"#22nsp-任务有必要吗\\" id=\\"markdown-toc-22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</a></li>\\n <li><a href=\\"#23针对两个或以上词组成的连续词的词义被丢失\\" id=\\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\\n <li><a href=\\"#24需要的算力高\\" id=\\"markdown-toc-24需要的算力高\\">2.4、需要的算力高</a></li>\\n <li><a href=\\"#25需要的模型大\\" id=\\"markdown-toc-25需要的模型大\\">2.5、需要的模型大</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四一些关于-bert-的问题\\" id=\\"markdown-toc-四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</a> <ul>\\n <li><a href=\\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\\" id=\\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\\n <li><a href=\\"#2为什么-bert-可以比-rnn-更好地并行化\\" id=\\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</h3>\\n\\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\\n\\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\\n\\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\\n\\n<h3 id=\\"二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</h3>\\n\\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\\n\\n<h4 id=\\"1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\\n\\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\\n\\n<p>更新 conda 到最新版本:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda update <span class=\\"nt\\">-n</span> base conda\\n</code></pre></div></div>\\n\\n<p>使用 Python 3.7 创建一个新的环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda create <span class=\\"nt\\">-n</span> py37 <span class=\\"nv\\">python</span><span class=\\"o\\">=</span>3.7\\n</code></pre></div></div>\\n\\n<p>激活这个新环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda activate py37\\n</code></pre></div></div>\\n\\n<p>验证正在使用的是正确版本的 Python</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>python <span class=\\"nt\\">--version</span>\\n</code></pre></div></div>\\n\\n<p>另外你可能还会用到的 conda 命令有:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\\nconda deactivate py37\\n\\n<span class=\\"c\\"># 查看 conda 当前安装的所有库</span>\\nconda list\\n</code></pre></div></div>\\n\\n<h4 id=\\"2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda <span class=\\"nb\\">install </span><span class=\\"nv\\">tensorflow</span><span class=\\"o\\">==</span>1.14.0\\n</code></pre></div></div>\\n\\n<p>验证 tensorflow 是否安装正确:</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">import</span> <span class=\\"nn\\">tensorflow</span> <span class=\\"k\\">as</span> <span class=\\"n\\">tf</span>\\n<span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"n\\">tf</span><span class=\\"p\\">.</span><span class=\\"n\\">__version__</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\\n\\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\\n\\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\\n\\n<ul>\\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n</ul>\\n\\n<p>4、安装 BERT 的服务端和客户端</p>\\n\\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\\n\\n<p>在你激活的环境里,安装 <code class=\\"language-plaintext highlighter-rouge\\">bert-as-service</code>:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 安装服务端和客户端</span>\\n<span class=\\"c\\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\\nconda <span class=\\"nb\\">install </span>bert-serving-server bert-serving-client \\n验证 bert-as-service 是否安装成功\\nbert-serving-start <span class=\\"nt\\">-h</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5启动-bert-服务端\\">5、启动 BERT 服务端</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 命令行下启动BERT服务</span>\\n<span class=\\"c\\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\\nbert-serving-start <span class=\\"nt\\">-model_dir</span> /模型/的/绝对/路径 <span class=\\"nt\\">-num_worker</span><span class=\\"o\\">=</span>4\\n</code></pre></div></div>\\n\\n<h4 id=\\"6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</h4>\\n\\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\\n\\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\\"language-plaintext highlighter-rouge\\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\\n\\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\\n\\n<h4 id=\\"7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</h4>\\n\\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">from</span> <span class=\\"nn\\">bert_serving.client</span> <span class=\\"kn\\">import</span> <span class=\\"n\\">BertClient</span>\\n<span class=\\"kn\\">import</span> <span class=\\"nn\\">numpy</span> <span class=\\"k\\">as</span> <span class=\\"n\\">np</span>\\n\\n<span class=\\"c1\\"># 定义类\\n</span><span class=\\"k\\">class</span> <span class=\\"nc\\">BertModel</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">__init__</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"k\\">try</span><span class=\\"p\\">:</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertClient</span><span class=\\"p\\">(</span><span class=\\"n\\">ip</span><span class=\\"o\\">=</span><span class=\\"s\\">'127.0.0.1'</span><span class=\\"p\\">,</span> <span class=\\"n\\">port</span><span class=\\"o\\">=</span><span class=\\"mi\\">5555</span><span class=\\"p\\">,</span> <span class=\\"n\\">port_out</span><span class=\\"o\\">=</span><span class=\\"mi\\">5556</span><span class=\\"p\\">)</span> <span class=\\"c1\\"># 创建客户端对象\\n</span> <span class=\\"c1\\"># 注意:可以参考API,查看其它参数的设置\\n</span> <span class=\\"c1\\"># 127.0.0.1 表示本机IP,也可以用localhost\\n</span> <span class=\\"k\\">except</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">raise</span> <span class=\\"nb\\">Exception</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cannot create BertClient\\"</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">close_bert</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">text</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''对输入文本进行embedding\\n Args:\\n text: str, 输入文本\\n Returns:\\n text_vector: float, 返回一个列表,包含text的embedding编码值\\n '''</span>\\n <span class=\\"n\\">text_vector</span> <span class=\\"o\\">=</span> <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">encode</span><span class=\\"p\\">([</span><span class=\\"n\\">text</span><span class=\\"p\\">])[</span><span class=\\"mi\\">0</span><span class=\\"p\\">]</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">text_vector</span> <span class=\\"c1\\"># 获取输出结果\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_1</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_2</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''根据两个语句的vector,计算它们的相似性\\n Args:\\n vec_1: float, 语句1的vector\\n vec_2: float, 语句2的vector\\n Returns:\\n sim_value: float, 返回相似性的计算值\\n '''</span>\\n <span class=\\"c1\\"># 根据cosine的计算公式\\n</span> <span class=\\"n\\">v1</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_1</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">v2</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">float</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span> <span class=\\"o\\">*</span> <span class=\\"n\\">v2</span><span class=\\"p\\">.</span><span class=\\"n\\">T</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span><span class=\\"p\\">)</span> <span class=\\"o\\">*</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">cosine</span> <span class=\\"o\\">=</span> <span class=\\"n\\">a</span> <span class=\\"o\\">/</span> <span class=\\"n\\">b</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">cosine</span>\\n\\n\\n<span class=\\"k\\">if</span> <span class=\\"n\\">__name__</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"__main__\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># 创建bert对象\\n</span> <span class=\\"n\\">bert</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertModel</span><span class=\\"p\\">()</span>\\n <span class=\\"k\\">while</span> <span class=\\"bp\\">True</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># --- 输入语句 ----\\n</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句1: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">if</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"N\\"</span> <span class=\\"ow\\">or</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"n\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">close_bert</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span> <span class=\\"k\\">break</span>\\n\\n <span class=\\"n\\">input_b</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句2: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># --- 对输入语句进行embedding ---\\n</span>\\n <span class=\\"n\\">a_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_a</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'a_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">a_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"n\\">b_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_b</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'b_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 计算两个语句的相似性\\n</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"n\\">a_vec</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'cosine value : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">cos</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'</span><span class=\\"se\\">\\\\n\\\\n</span><span class=\\"s\\">'</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\\n</span> <span class=\\"k\\">if</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">&gt;</span> <span class=\\"mf\\">0.85</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"2个语句的含义相似\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">else</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"不相似\\"</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<p>在使用 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 连接 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 时,你需要确保 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 使用的模型和 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\\n\\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>请输入语句1: \\n请输入语句2: \\n</code></pre></div></div>\\n\\n<p>两句输入好确认后,得到如下形式的结果:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>a_vec shape : (768,)\\nb_vec shape : (768,)\\ncosine value : 0.8691698561422959\\n</code></pre></div></div>\\n\\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\\n\\n<h3 id=\\"三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</h3>\\n\\n<p>论文地址:<a href=\\"https://arxiv.org/abs/1810.04805\\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\\n\\n<h4 id=\\"1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</h4>\\n\\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\\n\\n<blockquote>\\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\\n</blockquote>\\n\\n<h5 id=\\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\\n\\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\\n\\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\\n\\n<h5 id=\\"12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</h5>\\n\\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\\n\\n<h5 id=\\"13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</h5>\\n\\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\\n\\n<h4 id=\\"2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</h4>\\n\\n<h5 id=\\"21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\\n\\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\\n\\n<h5 id=\\"22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</h5>\\n\\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\\n\\n<h5 id=\\"23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\\n\\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\\n\\n<h5 id=\\"24需要的算力高\\">2.4、需要的算力高</h5>\\n\\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\\n\\n<h5 id=\\"25需要的模型大\\">2.5、需要的模型大</h5>\\n\\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\\n\\n<h3 id=\\"四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</h3>\\n\\n<h4 id=\\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\\n\\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\\n\\n<h4 id=\\"2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\\n\\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://arxiv.org/abs/1810.04805</li>\\n <li>https://github.com/google-research/bert</li>\\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"NLP":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\\n \\t<meta name=\\"description\\" content=\\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-24T15:08:01+00:00\\" class=\\"by-line\\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一自然语言处理领域近年的发展关键节点\\" id=\\"markdown-toc-一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</a> <ul>\\n <li><a href=\\"#1从理性主义到经验主义\\" id=\\"markdown-toc-1从理性主义到经验主义\\">1、从理性主义到经验主义</a></li>\\n <li><a href=\\"#2经验主义的早期还不是深度学习\\" id=\\"markdown-toc-2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</a></li>\\n <li><a href=\\"#3撇开特征让机器囫囵吞枣地学吧\\" id=\\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\\n <li><a href=\\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\" id=\\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\\n <li><a href=\\"#5自监督学习法让我们省去人工标注\\" id=\\"markdown-toc-5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</a></li>\\n <li><a href=\\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\\" id=\\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\\n <li><a href=\\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\" id=\\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\\n <li><a href=\\"#8数据和算力有了还不够\\" id=\\"markdown-toc-8数据和算力有了还不够\\">8、数据和算力有了,还不够</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二学术里程碑几篇重量级论文\\" id=\\"markdown-toc-二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</a> <ul>\\n <li><a href=\\"#1提出-transformer-的attention-is-all-you-need2017\\" id=\\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\\n <li><a href=\\"#2elmo-deep-contextualized-word-representations\\" id=\\"markdown-toc-2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</a></li>\\n <li><a href=\\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\" id=\\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\\n <li><a href=\\"#4gpt-3-language-models-are-few-shot-learners2020\\" id=\\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\\n <li><a href=\\"#其他的重量级论文\\" id=\\"markdown-toc-其他的重量级论文\\">其他的重量级论文</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三行业里程碑\\" id=\\"markdown-toc-三行业里程碑\\">三、行业里程碑</a></li>\\n <li><a href=\\"#四成本\\" id=\\"markdown-toc-四成本\\">四、成本</a></li>\\n <li><a href=\\"#五业内应用\\" id=\\"markdown-toc-五业内应用\\">五、业内应用</a></li>\\n <li><a href=\\"#五行业内哪些人的言论值得我们日常重点关注\\" id=\\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</h3>\\n\\n<p><img src=\\"/img/src/2022-12-17-ai-bert-1-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h4 id=\\"1从理性主义到经验主义\\">1、从理性主义到经验主义</h4>\\n\\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\\n\\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\\n\\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\\n\\n<h4 id=\\"2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</h4>\\n\\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\\n\\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\\n\\n<h4 id=\\"3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\\n\\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\\n\\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\\n\\n<h4 id=\\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\\n\\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\\n\\n<h4 id=\\"5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</h4>\\n\\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\\n\\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\\n\\n<h4 id=\\"6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\\n\\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\\n\\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\\n\\n<h4 id=\\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\\n\\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\\n\\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\\n\\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\\n\\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\\n\\n<h4 id=\\"8数据和算力有了还不够\\">8、数据和算力有了,还不够</h4>\\n\\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\\n\\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\\n\\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\\n\\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\\n\\n<h3 id=\\"二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</h3>\\n\\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\\n\\n<h4 id=\\"1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\\n\\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\\n\\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\\n\\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\\n\\n<h4 id=\\"2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</h4>\\n\\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\\n\\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\\n\\n<h4 id=\\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\\n\\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\\n\\n<ul>\\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\\n <li>在预训练中引入自监督学习任务。</li>\\n</ul>\\n\\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\\n\\n<h4 id=\\"4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\\n\\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\\n\\n<h4 id=\\"其他的重量级论文\\">其他的重量级论文</h4>\\n\\n<ul>\\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\\n <li>……</li>\\n</ul>\\n\\n<h3 id=\\"三行业里程碑\\">三、行业里程碑</h3>\\n\\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\\n\\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\\n\\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\\n\\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\\n\\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\\n\\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\\n\\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\\n\\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\\n\\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\\n\\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\\n\\n<h3 id=\\"四成本\\">四、成本</h3>\\n\\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\\n\\n<ul>\\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\\n</ul>\\n\\n<h3 id=\\"五业内应用\\">五、业内应用</h3>\\n\\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\\n\\n<ul>\\n <li>虚拟客服(可以乱真的)</li>\\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\\n <li>智能翻译</li>\\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\\n <li>AI 广告公司:替代传统广告公司</li>\\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\\n</ul>\\n\\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\\n\\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\\n\\n<ul>\\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-7.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-2.jpg\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-8.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年年底,西湖心辰公司发布「<a href=\\"https://www.heyfriday.cn/\\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-1.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</h3>\\n\\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\\n\\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\\n\\n<blockquote>\\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\\n</blockquote>\\n\\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\\n\\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\\n\\n<blockquote>\\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\\n</blockquote>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://beta.openai.com/docs/models</li>\\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\\n <li>https://hub.baai.ac.cn/view/21726</li>\\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\\n <li>https://www.sohu.com/a/615541698_121255906</li>\\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"自然语言处理":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\\n \\t<meta name=\\"description\\" content=\\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-24T15:08:01+00:00\\" class=\\"by-line\\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一自然语言处理领域近年的发展关键节点\\" id=\\"markdown-toc-一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</a> <ul>\\n <li><a href=\\"#1从理性主义到经验主义\\" id=\\"markdown-toc-1从理性主义到经验主义\\">1、从理性主义到经验主义</a></li>\\n <li><a href=\\"#2经验主义的早期还不是深度学习\\" id=\\"markdown-toc-2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</a></li>\\n <li><a href=\\"#3撇开特征让机器囫囵吞枣地学吧\\" id=\\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\\n <li><a href=\\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\" id=\\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\\n <li><a href=\\"#5自监督学习法让我们省去人工标注\\" id=\\"markdown-toc-5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</a></li>\\n <li><a href=\\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\\" id=\\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\\n <li><a href=\\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\" id=\\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\\n <li><a href=\\"#8数据和算力有了还不够\\" id=\\"markdown-toc-8数据和算力有了还不够\\">8、数据和算力有了,还不够</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二学术里程碑几篇重量级论文\\" id=\\"markdown-toc-二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</a> <ul>\\n <li><a href=\\"#1提出-transformer-的attention-is-all-you-need2017\\" id=\\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\\n <li><a href=\\"#2elmo-deep-contextualized-word-representations\\" id=\\"markdown-toc-2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</a></li>\\n <li><a href=\\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\" id=\\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\\n <li><a href=\\"#4gpt-3-language-models-are-few-shot-learners2020\\" id=\\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\\n <li><a href=\\"#其他的重量级论文\\" id=\\"markdown-toc-其他的重量级论文\\">其他的重量级论文</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三行业里程碑\\" id=\\"markdown-toc-三行业里程碑\\">三、行业里程碑</a></li>\\n <li><a href=\\"#四成本\\" id=\\"markdown-toc-四成本\\">四、成本</a></li>\\n <li><a href=\\"#五业内应用\\" id=\\"markdown-toc-五业内应用\\">五、业内应用</a></li>\\n <li><a href=\\"#五行业内哪些人的言论值得我们日常重点关注\\" id=\\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</h3>\\n\\n<p><img src=\\"/img/src/2022-12-17-ai-bert-1-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h4 id=\\"1从理性主义到经验主义\\">1、从理性主义到经验主义</h4>\\n\\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\\n\\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\\n\\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\\n\\n<h4 id=\\"2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</h4>\\n\\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\\n\\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\\n\\n<h4 id=\\"3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\\n\\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\\n\\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\\n\\n<h4 id=\\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\\n\\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\\n\\n<h4 id=\\"5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</h4>\\n\\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\\n\\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\\n\\n<h4 id=\\"6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\\n\\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\\n\\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\\n\\n<h4 id=\\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\\n\\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\\n\\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\\n\\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\\n\\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\\n\\n<h4 id=\\"8数据和算力有了还不够\\">8、数据和算力有了,还不够</h4>\\n\\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\\n\\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\\n\\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\\n\\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\\n\\n<h3 id=\\"二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</h3>\\n\\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\\n\\n<h4 id=\\"1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\\n\\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\\n\\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\\n\\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\\n\\n<h4 id=\\"2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</h4>\\n\\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\\n\\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\\n\\n<h4 id=\\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\\n\\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\\n\\n<ul>\\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\\n <li>在预训练中引入自监督学习任务。</li>\\n</ul>\\n\\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\\n\\n<h4 id=\\"4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\\n\\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\\n\\n<h4 id=\\"其他的重量级论文\\">其他的重量级论文</h4>\\n\\n<ul>\\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\\n <li>……</li>\\n</ul>\\n\\n<h3 id=\\"三行业里程碑\\">三、行业里程碑</h3>\\n\\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\\n\\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\\n\\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\\n\\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\\n\\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\\n\\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\\n\\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\\n\\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\\n\\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\\n\\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\\n\\n<h3 id=\\"四成本\\">四、成本</h3>\\n\\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\\n\\n<ul>\\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\\n</ul>\\n\\n<h3 id=\\"五业内应用\\">五、业内应用</h3>\\n\\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\\n\\n<ul>\\n <li>虚拟客服(可以乱真的)</li>\\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\\n <li>智能翻译</li>\\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\\n <li>AI 广告公司:替代传统广告公司</li>\\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\\n</ul>\\n\\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\\n\\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\\n\\n<ul>\\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-7.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-2.jpg\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-8.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年年底,西湖心辰公司发布「<a href=\\"https://www.heyfriday.cn/\\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-1.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</h3>\\n\\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\\n\\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\\n\\n<blockquote>\\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\\n</blockquote>\\n\\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\\n\\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\\n\\n<blockquote>\\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\\n</blockquote>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://beta.openai.com/docs/models</li>\\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\\n <li>https://hub.baai.ac.cn/view/21726</li>\\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\\n <li>https://www.sohu.com/a/615541698_121255906</li>\\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"]},"posts":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\\n \\t<meta name=\\"description\\" content=\\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-24T15:08:01+00:00\\" class=\\"by-line\\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一自然语言处理领域近年的发展关键节点\\" id=\\"markdown-toc-一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</a> <ul>\\n <li><a href=\\"#1从理性主义到经验主义\\" id=\\"markdown-toc-1从理性主义到经验主义\\">1、从理性主义到经验主义</a></li>\\n <li><a href=\\"#2经验主义的早期还不是深度学习\\" id=\\"markdown-toc-2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</a></li>\\n <li><a href=\\"#3撇开特征让机器囫囵吞枣地学吧\\" id=\\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\\n <li><a href=\\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\" id=\\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\\n <li><a href=\\"#5自监督学习法让我们省去人工标注\\" id=\\"markdown-toc-5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</a></li>\\n <li><a href=\\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\\" id=\\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\\n <li><a href=\\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\" id=\\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\\n <li><a href=\\"#8数据和算力有了还不够\\" id=\\"markdown-toc-8数据和算力有了还不够\\">8、数据和算力有了,还不够</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二学术里程碑几篇重量级论文\\" id=\\"markdown-toc-二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</a> <ul>\\n <li><a href=\\"#1提出-transformer-的attention-is-all-you-need2017\\" id=\\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\\n <li><a href=\\"#2elmo-deep-contextualized-word-representations\\" id=\\"markdown-toc-2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</a></li>\\n <li><a href=\\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\" id=\\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\\n <li><a href=\\"#4gpt-3-language-models-are-few-shot-learners2020\\" id=\\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\\n <li><a href=\\"#其他的重量级论文\\" id=\\"markdown-toc-其他的重量级论文\\">其他的重量级论文</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三行业里程碑\\" id=\\"markdown-toc-三行业里程碑\\">三、行业里程碑</a></li>\\n <li><a href=\\"#四成本\\" id=\\"markdown-toc-四成本\\">四、成本</a></li>\\n <li><a href=\\"#五业内应用\\" id=\\"markdown-toc-五业内应用\\">五、业内应用</a></li>\\n <li><a href=\\"#五行业内哪些人的言论值得我们日常重点关注\\" id=\\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</h3>\\n\\n<p><img src=\\"/img/src/2022-12-17-ai-bert-1-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h4 id=\\"1从理性主义到经验主义\\">1、从理性主义到经验主义</h4>\\n\\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\\n\\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\\n\\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\\n\\n<h4 id=\\"2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</h4>\\n\\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\\n\\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\\n\\n<h4 id=\\"3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\\n\\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\\n\\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\\n\\n<h4 id=\\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\\n\\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\\n\\n<h4 id=\\"5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</h4>\\n\\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\\n\\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\\n\\n<h4 id=\\"6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\\n\\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\\n\\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\\n\\n<h4 id=\\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\\n\\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\\n\\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\\n\\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\\n\\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\\n\\n<h4 id=\\"8数据和算力有了还不够\\">8、数据和算力有了,还不够</h4>\\n\\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\\n\\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\\n\\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\\n\\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\\n\\n<h3 id=\\"二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</h3>\\n\\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\\n\\n<h4 id=\\"1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\\n\\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\\n\\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\\n\\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\\n\\n<h4 id=\\"2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</h4>\\n\\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\\n\\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\\n\\n<h4 id=\\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\\n\\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\\n\\n<ul>\\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\\n <li>在预训练中引入自监督学习任务。</li>\\n</ul>\\n\\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\\n\\n<h4 id=\\"4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\\n\\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\\n\\n<h4 id=\\"其他的重量级论文\\">其他的重量级论文</h4>\\n\\n<ul>\\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\\n <li>……</li>\\n</ul>\\n\\n<h3 id=\\"三行业里程碑\\">三、行业里程碑</h3>\\n\\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\\n\\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\\n\\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\\n\\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\\n\\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\\n\\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\\n\\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\\n\\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\\n\\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\\n\\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\\n\\n<h3 id=\\"四成本\\">四、成本</h3>\\n\\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\\n\\n<ul>\\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\\n</ul>\\n\\n<h3 id=\\"五业内应用\\">五、业内应用</h3>\\n\\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\\n\\n<ul>\\n <li>虚拟客服(可以乱真的)</li>\\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\\n <li>智能翻译</li>\\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\\n <li>AI 广告公司:替代传统广告公司</li>\\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\\n</ul>\\n\\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\\n\\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\\n\\n<ul>\\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-7.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-2.jpg\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-8.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年年底,西湖心辰公司发布「<a href=\\"https://www.heyfriday.cn/\\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-1.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</h3>\\n\\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\\n\\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\\n\\n<blockquote>\\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\\n</blockquote>\\n\\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\\n\\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\\n\\n<blockquote>\\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\\n</blockquote>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://beta.openai.com/docs/models</li>\\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\\n <li>https://hub.baai.ac.cn/view/21726</li>\\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\\n <li>https://www.sohu.com/a/615541698_121255906</li>\\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\\n \\t<meta name=\\"description\\" content=\\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-17T15:08:01+00:00\\" class=\\"by-line\\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一关于-bert-的一些背景\\" id=\\"markdown-toc-一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</a></li>\\n <li><a href=\\"#二开始一个-bert-的动手小试验\\" id=\\"markdown-toc-二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</a> <ul>\\n <li><a href=\\"#1安装-anaconda-来为部署-bert-做环境准备\\" id=\\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\\n <li><a href=\\"#2安装-bert-所需要的各种依赖\\" id=\\"markdown-toc-2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</a></li>\\n <li><a href=\\"#3下载一个预训练pre-train过的-bert-模型\\" id=\\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\\n <li><a href=\\"#5启动-bert-服务端\\" id=\\"markdown-toc-5启动-bert-服务端\\">5、启动 BERT 服务端</a></li>\\n <li><a href=\\"#6在-pycharm-中使用-conda-的环境\\" id=\\"markdown-toc-6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</a></li>\\n <li><a href=\\"#7编写程序实现-bert-客户端\\" id=\\"markdown-toc-7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三bert-模型的优劣势及其原因\\" id=\\"markdown-toc-三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</a> <ul>\\n <li><a href=\\"#1bert-的优势是很明显的\\" id=\\"markdown-toc-1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</a> <ul>\\n <li><a href=\\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\" id=\\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\\n <li><a href=\\"#12识别并专注于较重要的部分进行文本处理\\" id=\\"markdown-toc-12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\\n <li><a href=\\"#13快速构建针对具体任务的-nlp-系统\\" id=\\"markdown-toc-13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2bert-模型的劣势及其原因\\" id=\\"markdown-toc-2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</a> <ul>\\n <li><a href=\\"#21随机挖-mask-的完形填空题是有隐患的\\" id=\\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\\n <li><a href=\\"#22nsp-任务有必要吗\\" id=\\"markdown-toc-22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</a></li>\\n <li><a href=\\"#23针对两个或以上词组成的连续词的词义被丢失\\" id=\\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\\n <li><a href=\\"#24需要的算力高\\" id=\\"markdown-toc-24需要的算力高\\">2.4、需要的算力高</a></li>\\n <li><a href=\\"#25需要的模型大\\" id=\\"markdown-toc-25需要的模型大\\">2.5、需要的模型大</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四一些关于-bert-的问题\\" id=\\"markdown-toc-四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</a> <ul>\\n <li><a href=\\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\\" id=\\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\\n <li><a href=\\"#2为什么-bert-可以比-rnn-更好地并行化\\" id=\\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</h3>\\n\\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\\n\\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\\n\\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\\n\\n<h3 id=\\"二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</h3>\\n\\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\\n\\n<h4 id=\\"1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\\n\\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\\n\\n<p>更新 conda 到最新版本:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda update <span class=\\"nt\\">-n</span> base conda\\n</code></pre></div></div>\\n\\n<p>使用 Python 3.7 创建一个新的环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda create <span class=\\"nt\\">-n</span> py37 <span class=\\"nv\\">python</span><span class=\\"o\\">=</span>3.7\\n</code></pre></div></div>\\n\\n<p>激活这个新环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda activate py37\\n</code></pre></div></div>\\n\\n<p>验证正在使用的是正确版本的 Python</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>python <span class=\\"nt\\">--version</span>\\n</code></pre></div></div>\\n\\n<p>另外你可能还会用到的 conda 命令有:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\\nconda deactivate py37\\n\\n<span class=\\"c\\"># 查看 conda 当前安装的所有库</span>\\nconda list\\n</code></pre></div></div>\\n\\n<h4 id=\\"2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda <span class=\\"nb\\">install </span><span class=\\"nv\\">tensorflow</span><span class=\\"o\\">==</span>1.14.0\\n</code></pre></div></div>\\n\\n<p>验证 tensorflow 是否安装正确:</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">import</span> <span class=\\"nn\\">tensorflow</span> <span class=\\"k\\">as</span> <span class=\\"n\\">tf</span>\\n<span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"n\\">tf</span><span class=\\"p\\">.</span><span class=\\"n\\">__version__</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\\n\\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\\n\\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\\n\\n<ul>\\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n</ul>\\n\\n<p>4、安装 BERT 的服务端和客户端</p>\\n\\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\\n\\n<p>在你激活的环境里,安装 <code class=\\"language-plaintext highlighter-rouge\\">bert-as-service</code>:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 安装服务端和客户端</span>\\n<span class=\\"c\\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\\nconda <span class=\\"nb\\">install </span>bert-serving-server bert-serving-client \\n验证 bert-as-service 是否安装成功\\nbert-serving-start <span class=\\"nt\\">-h</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5启动-bert-服务端\\">5、启动 BERT 服务端</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 命令行下启动BERT服务</span>\\n<span class=\\"c\\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\\nbert-serving-start <span class=\\"nt\\">-model_dir</span> /模型/的/绝对/路径 <span class=\\"nt\\">-num_worker</span><span class=\\"o\\">=</span>4\\n</code></pre></div></div>\\n\\n<h4 id=\\"6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</h4>\\n\\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\\n\\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\\"language-plaintext highlighter-rouge\\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\\n\\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\\n\\n<h4 id=\\"7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</h4>\\n\\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">from</span> <span class=\\"nn\\">bert_serving.client</span> <span class=\\"kn\\">import</span> <span class=\\"n\\">BertClient</span>\\n<span class=\\"kn\\">import</span> <span class=\\"nn\\">numpy</span> <span class=\\"k\\">as</span> <span class=\\"n\\">np</span>\\n\\n<span class=\\"c1\\"># 定义类\\n</span><span class=\\"k\\">class</span> <span class=\\"nc\\">BertModel</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">__init__</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"k\\">try</span><span class=\\"p\\">:</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertClient</span><span class=\\"p\\">(</span><span class=\\"n\\">ip</span><span class=\\"o\\">=</span><span class=\\"s\\">'127.0.0.1'</span><span class=\\"p\\">,</span> <span class=\\"n\\">port</span><span class=\\"o\\">=</span><span class=\\"mi\\">5555</span><span class=\\"p\\">,</span> <span class=\\"n\\">port_out</span><span class=\\"o\\">=</span><span class=\\"mi\\">5556</span><span class=\\"p\\">)</span> <span class=\\"c1\\"># 创建客户端对象\\n</span> <span class=\\"c1\\"># 注意:可以参考API,查看其它参数的设置\\n</span> <span class=\\"c1\\"># 127.0.0.1 表示本机IP,也可以用localhost\\n</span> <span class=\\"k\\">except</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">raise</span> <span class=\\"nb\\">Exception</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cannot create BertClient\\"</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">close_bert</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">text</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''对输入文本进行embedding\\n Args:\\n text: str, 输入文本\\n Returns:\\n text_vector: float, 返回一个列表,包含text的embedding编码值\\n '''</span>\\n <span class=\\"n\\">text_vector</span> <span class=\\"o\\">=</span> <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">encode</span><span class=\\"p\\">([</span><span class=\\"n\\">text</span><span class=\\"p\\">])[</span><span class=\\"mi\\">0</span><span class=\\"p\\">]</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">text_vector</span> <span class=\\"c1\\"># 获取输出结果\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_1</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_2</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''根据两个语句的vector,计算它们的相似性\\n Args:\\n vec_1: float, 语句1的vector\\n vec_2: float, 语句2的vector\\n Returns:\\n sim_value: float, 返回相似性的计算值\\n '''</span>\\n <span class=\\"c1\\"># 根据cosine的计算公式\\n</span> <span class=\\"n\\">v1</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_1</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">v2</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">float</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span> <span class=\\"o\\">*</span> <span class=\\"n\\">v2</span><span class=\\"p\\">.</span><span class=\\"n\\">T</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span><span class=\\"p\\">)</span> <span class=\\"o\\">*</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">cosine</span> <span class=\\"o\\">=</span> <span class=\\"n\\">a</span> <span class=\\"o\\">/</span> <span class=\\"n\\">b</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">cosine</span>\\n\\n\\n<span class=\\"k\\">if</span> <span class=\\"n\\">__name__</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"__main__\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># 创建bert对象\\n</span> <span class=\\"n\\">bert</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertModel</span><span class=\\"p\\">()</span>\\n <span class=\\"k\\">while</span> <span class=\\"bp\\">True</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># --- 输入语句 ----\\n</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句1: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">if</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"N\\"</span> <span class=\\"ow\\">or</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"n\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">close_bert</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span> <span class=\\"k\\">break</span>\\n\\n <span class=\\"n\\">input_b</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句2: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># --- 对输入语句进行embedding ---\\n</span>\\n <span class=\\"n\\">a_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_a</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'a_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">a_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"n\\">b_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_b</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'b_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 计算两个语句的相似性\\n</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"n\\">a_vec</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'cosine value : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">cos</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'</span><span class=\\"se\\">\\\\n\\\\n</span><span class=\\"s\\">'</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\\n</span> <span class=\\"k\\">if</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">&gt;</span> <span class=\\"mf\\">0.85</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"2个语句的含义相似\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">else</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"不相似\\"</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<p>在使用 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 连接 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 时,你需要确保 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 使用的模型和 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\\n\\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>请输入语句1: \\n请输入语句2: \\n</code></pre></div></div>\\n\\n<p>两句输入好确认后,得到如下形式的结果:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>a_vec shape : (768,)\\nb_vec shape : (768,)\\ncosine value : 0.8691698561422959\\n</code></pre></div></div>\\n\\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\\n\\n<h3 id=\\"三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</h3>\\n\\n<p>论文地址:<a href=\\"https://arxiv.org/abs/1810.04805\\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\\n\\n<h4 id=\\"1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</h4>\\n\\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\\n\\n<blockquote>\\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\\n</blockquote>\\n\\n<h5 id=\\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\\n\\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\\n\\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\\n\\n<h5 id=\\"12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</h5>\\n\\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\\n\\n<h5 id=\\"13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</h5>\\n\\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\\n\\n<h4 id=\\"2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</h4>\\n\\n<h5 id=\\"21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\\n\\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\\n\\n<h5 id=\\"22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</h5>\\n\\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\\n\\n<h5 id=\\"23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\\n\\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\\n\\n<h5 id=\\"24需要的算力高\\">2.4、需要的算力高</h5>\\n\\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\\n\\n<h5 id=\\"25需要的模型大\\">2.5、需要的模型大</h5>\\n\\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\\n\\n<h3 id=\\"四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</h3>\\n\\n<h4 id=\\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\\n\\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\\n\\n<h4 id=\\"2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\\n\\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://arxiv.org/abs/1810.04805</li>\\n <li>https://github.com/google-research/bert</li>\\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\\n \\t<meta name=\\"description\\" content=\\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-11T15:59:57+00:00\\" class=\\"by-line\\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2022-12-11-wechat-chatgpt-3.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\\n\\n<h3 id=\\"stepbystep\\">Step by step</h3>\\n\\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\\n\\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\\n\\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\\n\\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\\n\\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\\n\\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\\n\\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">chatGPTAccountPool</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"na\\">email</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your email&gt;</span>\\n <span class=\\"na\\">password</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your password&gt;</span>\\n<span class=\\"c1\\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\\n<span class=\\"na\\">chatPrivateTiggerKeyword</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker pull holegots/wechat-chatgpt:latest。\\n</code></pre></div></div>\\n\\n<p>第八步,启动 wechat-chatgpt:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker run <span class=\\"nt\\">-d</span> <span class=\\"nt\\">--name</span> wechat-chatgpt <span class=\\"nt\\">-v</span> <span class=\\"si\\">$(</span><span class=\\"nb\\">pwd</span><span class=\\"si\\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\\n</code></pre></div></div>\\n\\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\\"language-plaintext highlighter-rouge\\">npm install &amp;&amp; poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-1.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-2.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\\n\\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker logs <span class=\\"nt\\">-f</span> wechat-chatgpt\\n</code></pre></div></div>\\n\\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\\n\\n<p>如果要停止运行,用如下命令:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker stop wechat-chatgpt\\n</code></pre></div></div>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<p>1、<a href=\\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>不要船开远了,就忘了为什么启航</title>\\n \\t<meta name=\\"description\\" content=\\"2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>不要船开远了,就忘了为什么启航</h2>\\t\\t\\n\\t<time datetime=\\"2022-08-11T15:53:57+00:00\\" class=\\"by-line\\">11 Aug 2022, 杭州 | 作者 麦克船长 | 总计 3223 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>偶然翻到 2020.06.11 刚来到阿里时写的一篇内容(我是 2020 年的 6 月 4 日我入职阿里巴巴集团),是有关于来阿里的期待、对这家公司的一些粗浅初步的理解。此时再翻来看看,最大的感触就是,提醒自己勿忘初心。</p>\\n\\n<p>在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:</p>\\n\\n<ul>\\n <li>很多人是带着梦想来阿里的,那么我的梦想是什么呢?</li>\\n <li>最喜欢新六脉的哪句话?为什么?</li>\\n <li>关于阿里企业价值观:为什么要接受这套价值观?</li>\\n <li>价值观的本质意义(极度务实视角)是什么?</li>\\n <li>Landing 的 SOP</li>\\n <li>问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2020 年平安夜 · 百年湖畔 87 期合影</p>\\n\\n<h3 id=\\"很多人是带着梦想来阿里的那么我的梦想是什么呢\\">很多人是带着梦想来阿里的,那么我的梦想是什么呢?</h3>\\n\\n<p>Christensen 在《创新者的窘境》中提到:每一次技术更迭,都需要破坏性创新,而破坏性创新在前一次技术更迭的胜出者内部是很难生长出来的。阿里诞生以来,不断地创造第二增长曲线:阿里巴巴、淘宝、支付宝、天猫、阿里云、钉钉 …… 这让我非常好奇。其中很多产品穿越多个时间周期,期间不断创造内生二次曲线。</p>\\n\\n<p>但是阿里也一样错失了很多,微信、美团、拼多多、抖/快…… 等等很多产品诞生在了其他公司,还有某些产品在不断的科技更迭中自身生长出了第二曲线。</p>\\n\\n<p>因此我来阿里的梦想也非常明确:<strong>参与或创造一次(甚至多次)第二曲线,可以是新产品,也可以是原有产品内生的。在这个过程中获得个人成长、个人价值。</strong></p>\\n\\n<p>一直以来,我有三个最想实现或得到的东西:LOVE、CREATION、FREEDOM。随着生活与工作的前行,对这三者的理解,在不断加深。在这个问题里,我想应该是讨论”CREATION”。</p>\\n\\n<p>CREATION 上,我的梦想的范式,大概是从自己中学时代就确立了,在某一次人类社会变革浪潮中,扮演有一定权重的角色。这里面有几个变量:<strong>什么领域(F)的变革;什么规模(S)的变革;多大的权重(W);什么角色(R)。</strong></p>\\n\\n<p>F 这个变量,我在中学及大学时代逐渐明确,是以相对普适的产品形式输出结果并对社会变革产生积极作用。后来越来越明确为科技与商业结合的领域。</p>\\n\\n<p>S、W 这两个变量,自然是越大越好。因此我会希望能够构建尽可能大的机会,或者参与到尽可能大的机会中。R 希望是有强烈 Ownership 的身份。</p>\\n\\n<p>因此过去几年我选择了创业。创业就像冲浪,你抓住一次浪并完成漂亮的动作,就是一次不算失败的创业。但是如果一个浪没抓住,你去追它是没意义的,而应该等待下一个浪。我认为在未来 5~10 年内难以出现规模能大到令我足够兴奋的科技浪潮。大浪潮中属于创业者的大机会很多,而中小浪潮的大机会基本只属于大平台,那么为了在壮年期做获得我的 CREATION,我选择了加入阿里这样的大平台。</p>\\n\\n<p>在最后做决定以及初来阿里的那个人生转折点,作为老阿里人的曲洋老师对我说的一句话,深深地鼓励了我,他说:”带着创业气质,把这里当你的舞台折腾!”</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2020 年双十一 · 淘宝 KO</p>\\n\\n<h3 id=\\"最喜欢新六脉的哪句话为什么\\">最喜欢新六脉的哪句话?为什么?</h3>\\n\\n<p>最喜欢的是“因为信任所以简单”。</p>\\n\\n<p>我一直认为人最重要的两个元特质是”真实”和“谦逊”,由”真实”可以塑造自我(对内)、构建信任(对外),后者可以带来清晰的边界,继而实现人与人之间高效的互动(这种互动包括各种人际关系在内,如婚姻、合伙、共事、合作等等)。</p>\\n\\n<p><strong>事物(虚实皆可)呈现在人的认知中,会得到三方面的投影:facts、opinion、feeling。如果我们足够真实,当我们需要把这三方面呈现给他人时,双方就能顺畅建立信任。信任的结果,就对应到这三方面:彼此之间建立共识(facts)、求同存异(opinion)、尊重感受(feeling),这就是”简单”。</strong></p>\\n\\n<p>另外一句是鼓励自己勇于绽放的一条:「此次此刻,非我莫属」。</p>\\n\\n<p>激情、自信、积极…… 通常行为统一表现为“勇于绽放自己”,绽放有表达(语言)与投身(行为)两种表现形式。更进一步推进就是”此次此刻,非我莫属”的阿里价值观。</p>\\n\\n<p>低调、稳重、谦逊,其实与“此次此刻,非我莫属“,并不矛盾。这点是我来到阿里后,发现自己在过去这些年的创业中已经不知不觉改变了,从 Introvert 逐渐变成了 Extrovert 的人,而且从曾经 social 中消耗能量,逐渐变为我现在可以感知到获得能量。这种变化,是我最近来阿里才确认发生的,此前因为自己创业者的身份没有察觉这种变化的发生。</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年秋 · 径山之行</p>\\n\\n<h3 id=\\"关于阿里企业价值观为什么要接受这套价值观\\">关于阿里企业价值观:为什么要接受这套价值观?</h3>\\n\\n<p>马老师和老逍都提到这个:我们是寻找同路人,而不是教育别人。这其实非常明晰地解释了为什么阿里要构建一个毛细血管网络一样的政委体系。基于这种用人理念,政委体系不敢说是最优解,但一定是优解(而且是否有更优解的论证没有意义)。</p>\\n\\n<p>对于个人,我的理解是要做两件事:<strong>1)构建自己的价值观体系(初始化);2)寻找价值观契合的公司(做匹配)。这两点里,没有任何地方提到”你要改变价值观为了契合你所在的公司”。</strong></p>\\n\\n<p>而企业价值观呢,其实可以分两部分看待:普世价值观、独特价值观。前者因为是普世的,所以到了哪个公司这种价值观都对,这点老逍也提了,比如“客户第一”。后者是个性化的,但不存在孰高孰低,就像一个人内向还是外向,你不能说哪个是错的。</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-4.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年双十一 · 天天特卖团队</p>\\n\\n<h3 id=\\"价值观的本质意义极度务实视角是什么\\">价值观的本质意义(极度务实视角)是什么?</h3>\\n\\n<p><strong>在充分考虑价值观适配使命、愿景基础上,价值观本身的意义,在和风细雨时(即企业价值观与其他价值判断相 match 时),是看不到的。但在暴风骤雨时(即企业价值观与其他价值判断相冲突时),就能显示其实实在在的作用了。</strong>我认为包括三类,前两个是阿里整体视角,第三个是阿里内部:</p>\\n\\n<ul>\\n <li>经济体内,阿里与其他生态位的冲突或损益关系,如曾经的美蘑口一役。</li>\\n <li>经济体内,其他的生态位之间的冲突或损益关系,如曾经的十月围城。</li>\\n <li>阿里人的行为价值判断,如最近的钉钉代考事件、过往的各类廉政事件。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-5.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年冬 · 淘宝天猫合并前合影</p>\\n\\n<h3 id=\\"landing的sop\\">Landing 的 SOP</h3>\\n\\n<p>大家都说 landing 充满挑战,马老师其实给出了 landing 的 SOP 三部曲:<strong>一起打过仗、一起创过新、一起度过难。三个经历都 close 才算 smooth landing。</strong></p>\\n\\n<p>集团人才策略层面、HR 实操层面、Leader 层面、,对于新人 landing 能做到什么程度的保障,其实每个新人感受到的不尽相同:</p>\\n\\n<ul>\\n <li>集团层面,始终是在构建更好的新人 landing 环境的,这符合自身价值,这能打下很好的底子。</li>\\n <li>实操层面,包括面试阶段对候选人的价值观判断、预期管理,面试及入职后公司文化及人才体系的事实呈现、内化吸收和长期解惑。</li>\\n <li>Leader 层面,这是新人体感最强烈的部分,也是最重要的部分。尽管拥抱变化,但首先 Leader 需要给出尽可能最全面的考虑,其次是对候选人的预期管理。好的 Leader 会给候选人提供合理的着陆点、多个降落伞、缓冲垫,完成 smooth landing。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-6.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年夏 · 出差厦漳泉</p>\\n\\n<h3 id=\\"问问自己来到阿里如果初期我可能需要做一点改变那会是什么\\">问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</h3>\\n\\n<p>曾经个人的激情与动力,常来自于“增长”。常说<strong>高增长掩盖一切</strong>,所以未来在阿里如果不能如创业般快速获得反馈得到积极结果,并且大平台中必然要接受大量关联方共同参与项目而导致的效率降低,因此我要逐渐改变自己,重新适应这种环境下的激情与动力获得方式。</p>\\n\\n<p>另一方面,作为创业公司的负责人,工作中鲜有因为内部原因而无法推进的事情,但是扮演肩部或腰部角色时,需要接受头部决策的一定程度不可控,这是我需要作出的适应与改变。关于这一点,我在几个月前就已经在做预期管理和心态调整,我认为以创业者的强适应性,这可能并不会是问题,但是我习惯于保持谨慎的乐观来面对自己。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的 Jekyll 快速教程</title>\\n \\t<meta name=\\"description\\" content=\\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的 Jekyll 快速教程</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-23T19:43:02+00:00\\" class=\\"by-line\\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\\n\\n<h3 id=\\"part-1基本特点\\">Part 1、基本特点</h3>\\n\\n<h4 id=\\"一基本语法\\">一、基本语法</h4>\\n\\n<ul>\\n <li>变量:用双大括号表示变量 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code></li>\\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\\n <li>支持循环与分支结构:比如 <code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">if-elsif-else-endif</code> :可以使用 <code class=\\"language-plaintext highlighter-rouge\\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\\n</ul>\\n\\n<h4 id=\\"二典型-jekyll-项目结构及重要文件介绍\\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\\n\\n<h5 id=\\"1配置文件-_configyml\\">1、配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code></h5>\\n\\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\\"language-plaintext highlighter-rouge\\">encoding: utf-8</code> 这一条。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Site settings</span>\\n<span class=\\"na\\">encoding</span><span class=\\"pi\\">:</span> <span class=\\"s\\">utf-8</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">麦克船长的技术、产品与商业博客</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\\"</span>\\n<span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptain.com\\"</span>\\n<span class=\\"na\\">author</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">name</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Your</span><span class=\\"nv\\"> </span><span class=\\"s\\">Name\\"</span>\\n <span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptian.com\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Markdown and highlighter</span>\\n<span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Plugins</span>\\n<span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-paginate</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-sitemap</span>\\n</code></pre></div></div>\\n\\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Build settings</span>\\n<span class=\\"na\\">baseurl</span><span class=\\"pi\\">:</span> <span class=\\"c1\\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\\n<span class=\\"na\\">source</span><span class=\\"pi\\">:</span> <span class=\\"s\\">.</span>\\n<span class=\\"na\\">destination</span><span class=\\"pi\\">:</span> <span class=\\"s\\">./_site</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:title</span>\\n<span class=\\"na\\">paginate</span><span class=\\"pi\\">:</span> <span class=\\"m\\">20</span>\\n<span class=\\"na\\">paginate_path</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/page:num/</span>\\n<span class=\\"na\\">collections</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">posts</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">output</span><span class=\\"pi\\">:</span> <span class=\\"no\\">true</span>\\n <span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:year/:month/:day/:title/</span>\\n <span class=\\"na\\">directory</span><span class=\\"pi\\">:</span> <span class=\\"s\\">_posts</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2布局文件_layouts-目录下的文件规则\\">2、布局文件:<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的文件规则</h5>\\n\\n<p>Jekyll 的 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\\"language-plaintext highlighter-rouge\\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\\"language-plaintext highlighter-rouge\\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\\n\\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\\n\\n<h5 id=\\"3页面文件及其头部\\">3、页面文件及其头部</h5>\\n\\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">page</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/categories/</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">Categories</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>每个页面文件的头部都会有layout,并与 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的某个文件对应。</p>\\n\\n<h3 id=\\"part-2jekyll-中的全局变量\\">Part 2、Jekyll 中的全局变量</h3>\\n\\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:包含当前页面或文章的内容。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:包含有关网站的所有标签的信息。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\\n</ul>\\n\\n<p>这些变量可以在模板中使用,比如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;h1&gt;</span>{{ page.title }}<span class=\\"nt\\">&lt;/h1&gt;</span>\\n<span class=\\"nt\\">&lt;p&gt;</span>{{ site.description }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n<span class=\\"nt\\">&lt;ul&gt;</span>\\n {{ for category in site.categories %}\\n <span class=\\"nt\\">&lt;li&gt;</span>{{ category }}<span class=\\"nt\\">&lt;/li&gt;</span>\\n {{ endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span> \\n</code></pre></div></div>\\n\\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">my_custom_variable</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Hello</span><span class=\\"nv\\"> </span><span class=\\"s\\">World\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后就可以在模板文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\\n\\n<h4 id=\\"一site变量\\">一、site变量</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\\n\\n<h5 id=\\"1sitecategories\\">1、<code class=\\"language-plaintext highlighter-rouge\\">site.categories</code></h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\\"language-plaintext highlighter-rouge\\">first</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">category</code> 的名字,如下使用:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2sitepages\\">2、site.pages</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3其他常用属性\\">3、其他常用属性</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.title</code>:是网站的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.related_posts</code>:相关文章的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.data</code>:从 <code class=\\"language-plaintext highlighter-rouge\\">_data</code> 目录加载的数据。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.static_files</code>:静态文件的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.collections</code>:自定义集合的列表。</li>\\n</ul>\\n\\n<h4 id=\\"二page-变量\\">二、<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量</h4>\\n\\n<p>在 Jekyll 中,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量属性包括:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">layout</code>:表示页面使用的布局模板的名称。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">title</code>:表示页面的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">date</code>:表示页面的发布日期。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">categories</code>:表示页面所属的分类列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:表示页面所属的标签列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\\n</ul>\\n\\n<p>在模板中,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.属性名 }}</code> 的方式来访问 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.title }}</code>。此外,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量还有其他属性,如 <code class=\\"language-plaintext highlighter-rouge\\">permalink</code>、<code class=\\"language-plaintext highlighter-rouge\\">excerpt</code>、<code class=\\"language-plaintext highlighter-rouge\\">url</code> 等,可以根据需要调用。</p>\\n\\n<h3 id=\\"part-3控制结构\\">Part 3、控制结构</h3>\\n\\n<h4 id=\\"1if-else-分支结构\\">1、<code class=\\"language-plaintext highlighter-rouge\\">if-else</code> 分支结构</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ if tmp_var == \\"type1\\" %}\\n{{ elsif tmp_var == \\"type2\\" %}\\n{{ elsif tmp_var == \\"type3\\" %}\\n{{ elsif tmp_var == \\"type4\\" %}\\n{{ else tmp_var == \\"type5\\" %}\\n{{ endif %}\\n</code></pre></div></div>\\n\\n<h4 id=\\"2for-endfor-循环结构\\">2、<code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 循环结构</h4>\\n\\n<p>不带条件判断的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 循环如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for post in paginator.posts %}\\n\\t<span class=\\"c\\">&lt;!-- Your other sentences --&gt;</span>\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<p>带条件循环的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages | where: \\"dir\\", \\"categories\\" %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3jekyll-支持的其他结构包括\\">3、Jekyll 支持的其他结构包括:</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">capture</code> 用于捕获输出的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">cycle</code> 用于循环一组字符串的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">include</code> 用于包含其他文件的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">while</code> 用于在满足指定条件时执行代码的结构。</li>\\n</ul>\\n\\n<h3 id=\\"参考\\">参考:</h3>\\n\\n<p>1、<a href=\\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\\n2、<a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\\n \\t<meta name=\\"description\\" content=\\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-21T15:53:57+00:00\\" class=\\"by-line\\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#写在前面\\" id=\\"markdown-toc-写在前面\\">写在前面</a></li>\\n <li><a href=\\"#1github上的准备\\" id=\\"markdown-toc-1github上的准备\\">1、GitHub 上的准备</a></li>\\n <li><a href=\\"#2了解ruby和jekyll\\" id=\\"markdown-toc-2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</a></li>\\n <li><a href=\\"#3了解gem\\" id=\\"markdown-toc-3了解gem\\">3、了解 Gem</a></li>\\n <li><a href=\\"#4安装homebrew\\" id=\\"markdown-toc-4安装homebrew\\">4、安装 Homebrew</a></li>\\n <li><a href=\\"#5用homebrew安装ruby\\" id=\\"markdown-toc-5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</a></li>\\n <li><a href=\\"#6安装jekyll和bundler\\" id=\\"markdown-toc-6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</a></li>\\n <li><a href=\\"#7使用bundle管理包依赖关系\\" id=\\"markdown-toc-7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</a></li>\\n <li><a href=\\"#8本地启动一下看看\\" id=\\"markdown-toc-8本地启动一下看看\\">8、本地启动一下看看</a></li>\\n <li><a href=\\"#9用jekyll创建一个项目\\" id=\\"markdown-toc-9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</a></li>\\n <li><a href=\\"#10修改gemfile文件\\" id=\\"markdown-toc-10修改gemfile文件\\">10、修改 Gemfile 文件</a></li>\\n <li><a href=\\"#11配置githubpages\\" id=\\"markdown-toc-11配置githubpages\\">11、配置 Github Pages</a></li>\\n <li><a href=\\"#12配置一个jekylltheme\\" id=\\"markdown-toc-12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</a></li>\\n <li><a href=\\"#13设置自定义域名\\" id=\\"markdown-toc-13设置自定义域名\\">13、设置自定义域名</a></li>\\n <li><a href=\\"#14用-rouge-实现代码高亮\\" id=\\"markdown-toc-14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</a></li>\\n <li><a href=\\"#15一些扩展问题\\" id=\\"markdown-toc-15一些扩展问题\\">15、一些扩展问题</a> <ul>\\n <li><a href=\\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\" id=\\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\\n <li><a href=\\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\" id=\\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\\n <li><a href=\\"#q3如何为每篇文章添加一个目录\\" id=\\"markdown-toc-q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</a></li>\\n <li><a href=\\"#q4如何在-jekyll-中支持-katex\\" id=\\"markdown-toc-q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\\n <li><a href=\\"#在-githubio-上\\" id=\\"markdown-toc-在-githubio-上\\">在 GitHub.io 上</a></li>\\n <li><a href=\\"#如果不在-githubio-上则还需要额外工作\\" id=\\"markdown-toc-如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\\n <li><a href=\\"#使用示例\\" id=\\"markdown-toc-使用示例\\">使用示例</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#q5jekyll-中如何支持-graphviz-\\" id=\\"markdown-toc-q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\\n <li><a href=\\"#q6如何显示--或者--\\" id=\\"markdown-toc-q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#参考\\" id=\\"markdown-toc-参考\\">参考</a></li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\\n\\n<ul>\\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\\n</ul>\\n\\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\\n\\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\\n\\n<h3 id=\\"1github上的准备\\">1、GitHub 上的准备</h3>\\n\\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git clone https://github.com/username/username.github.io\\n</code></pre></div></div>\\n\\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git add <span class=\\"nt\\">--all</span>\\n<span class=\\"nv\\">$ </span>git commit <span class=\\"nt\\">-m</span> <span class=\\"s2\\">\\"Initial commit\\"</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</h3>\\n\\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\\n\\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\\n\\n<h3 id=\\"3了解gem\\">3、了解 Gem</h3>\\n\\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\\n\\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\\ngem sources -l\\n</code></pre></div></div>\\n\\n<h3 id=\\"4安装homebrew\\">4、安装 Homebrew</h3>\\n\\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>/bin/bash <span class=\\"nt\\">-c</span> <span class=\\"s2\\">\\"</span><span class=\\"si\\">$(</span>curl <span class=\\"nt\\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\\"si\\">)</span><span class=\\"s2\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\\n\\n<h3 id=\\"5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</h3>\\n\\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>brew <span class=\\"nb\\">install </span>chruby ruby-install xz\\n</code></pre></div></div>\\n\\n<p>安装 Ruby 的最新版本:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby-install ruby\\n</code></pre></div></div>\\n\\n<p>这时候提示如下问题:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"o\\">&gt;&gt;&gt;</span> Updating ruby versions ...\\n<span class=\\"o\\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\\"se\\">\\\\</span>\\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\\n<span class=\\"o\\">!!!</span> Failed to download ruby versions!\\n</code></pre></div></div>\\n\\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ echo \\"185.199.111.133 raw.githubusercontent.com\\" &gt;&gt; /etc/hosts\\n</code></pre></div></div>\\n\\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/chruby.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/auto.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"chruby ruby-3.1.2\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc <span class=\\"c\\"># run 'chruby' to see actual version</span>\\n</code></pre></div></div>\\n\\n<p>再看下 Ruby 版本对不对:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby <span class=\\"nt\\">-v</span>\\n</code></pre></div></div>\\n\\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\\n\\n<h3 id=\\"6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"nb\\">install </span>jekyll bundler\\n</code></pre></div></div>\\n\\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\\n\\n<h3 id=\\"7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</h3>\\n\\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">source</span> <span class=\\"s1\\">'https://rubygems.org'</span>\\ngem <span class=\\"s1\\">'nokogiri'</span>\\ngem <span class=\\"s1\\">'rack'</span>, <span class=\\"s1\\">'~&gt; 2.2.4'</span>\\ngem <span class=\\"s1\\">'rspec'</span>\\ngem <span class=\\"s1\\">'jekyll'</span>\\n</code></pre></div></div>\\n\\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ git add Gemfile Gemfile.lock\\n</code></pre></div></div>\\n\\n<h3 id=\\"8本地启动一下看看\\">8、本地启动一下看看</h3>\\n\\n<p>先用 bundle 如下命令来启动:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: none\\n Source: /Users/captain/Workspace/poechant.github.io\\n Destination: /Users/captain/Workspace/poechant.github.io/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n done in 0.014 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\\n Server address: http://127.0.0.1:4000\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git pull <span class=\\"nt\\">--no-rebase</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll new CaptainMikeBlog\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>CaptainMikeBlog\\n<span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n Jekyll Feed: Generating feed for posts\\n done in 0.365 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\\n Server address: http://127.0.0.1:4000/\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"10修改gemfile文件\\">10、修改 Gemfile 文件</h3>\\n\\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"s2\\">\\"github-pages\\"</span>, <span class=\\"s2\\">\\"~&gt; GITHUB-PAGES-VERSION\\"</span>, group: :jekyll_plugins\\n</code></pre></div></div>\\n\\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ bundle install\\n</code></pre></div></div>\\n\\n<p>再本地启动服务器测试:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>得到如下提示:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\\nPrepending <span class=\\"sb\\">`</span>bundle <span class=\\"nb\\">exec</span><span class=\\"sb\\">`</span> to your <span class=\\"nb\\">command </span>may solve this. <span class=\\"o\\">(</span>Gem::LoadError<span class=\\"o\\">)</span>\\n</code></pre></div></div>\\n\\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle add webrick\\n<span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\\n\\n<h3 id=\\"11配置githubpages\\">11、配置 Github Pages</h3>\\n\\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>baseurl: \\"\\"\\nurl: \\"http://your-username.github.io\\"\\n</code></pre></div></div>\\n\\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\\n\\n<h3 id=\\"12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</h3>\\n\\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_includes\\n_layouts\\n_sass\\ncss\\njs\\nimg\\n404.markdown\\nindex.html\\n</code></pre></div></div>\\n\\n<p>不同主题会有所不同,这里只列个大概。</p>\\n\\n<h3 id=\\"13设置自定义域名\\">13、设置自定义域名</h3>\\n\\n<p>添加四条 A 记录,记录值如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>185.199.108.153\\n185.199.109.153\\n185.199.110.153\\n185.199.111.153\\n</code></pre></div></div>\\n\\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\\n\\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\\n\\n<h3 id=\\"14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</h3>\\n\\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem install kramdom rouge\\n</code></pre></div></div>\\n\\n<p>然后配置 _config.yml 文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>然后用 rouge 创建 syntax.css 文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>rougify style github <span class=\\"o\\">&gt;</span> css/syntax.css\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">_include/head.html</code> 文件中添加:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/css/syntax.css\\"</span> <span class=\\"nt\\">/&gt;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"15一些扩展问题\\">15、一些扩展问题</h3>\\n\\n<h4 id=\\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\\n\\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是一篇文章</span>\\n<span class=\\"na\\">excerpt</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是文章的摘要</span>\\n<span class=\\"nn\\">---</span>\\n\\n这是文章的正文内容\\n</code></pre></div></div>\\n\\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;ul&gt;</span>\\n {% for post in paginator.posts %}\\n <span class=\\"nt\\">&lt;li&gt;</span>\\n <span class=\\"nt\\">&lt;h2&gt;&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{{ post.url }}\\"</span><span class=\\"nt\\">&gt;</span>{{ post.title }}<span class=\\"nt\\">&lt;/a&gt;&lt;/h2&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>{{ post.excerpt }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n <span class=\\"nt\\">&lt;/li&gt;</span>\\n {% endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\\n\\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\\n\\n<h4 id=\\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\\n\\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code>目录下创建一个<code class=\\"language-plaintext highlighter-rouge\\">category.html</code>文件:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>---\\nlayout: default\\n---\\n\\n<span class=\\"nt\\">&lt;div</span> <span class=\\"na\\">class=</span><span class=\\"s\\">\\"container\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n {% if site.categories[page.category] %}\\n {% for post in site.categories[page.category] %}\\n <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{% if site.baseurl == \\"</span><span class=\\"err\\">/\\"</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">else</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">|</span> <span class=\\"na\\">prepend:</span> <span class=\\"na\\">site.baseurl</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">endif</span> <span class=\\"err\\">%}\\"</span><span class=\\"nt\\">&gt;</span>\\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\\n <span class=\\"nt\\">&lt;/a&gt;</span>\\n {% endfor %}\\n {% else %}\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>No posts for this category. If you have something in mind, check <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/write\\"</span><span class=\\"nt\\">&gt;</span>Write For Us<span class=\\"nt\\">&lt;/a&gt;</span>page.<span class=\\"nt\\">&lt;/p&gt;</span>\\n {% endif %}\\n<span class=\\"nt\\">&lt;/div&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">include</span><span class=\\"pi\\">:</span> <span class=\\"pi\\">[</span><span class=\\"s1\\">'</span><span class=\\"s\\">_categories'</span><span class=\\"pi\\">]</span>\\n</code></pre></div></div>\\n\\n<p>在根目录创建一个<code class=\\"language-plaintext highlighter-rouge\\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\\"language-plaintext highlighter-rouge\\">ai.html</code>如下:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">category</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">人工智能</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s\\">This is the description.</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/category/ai</span>\\n<span class=\\"na\\">category</span><span class=\\"pi\\">:</span> <span class=\\"s\\">ai</span>\\n<span class=\\"na\\">category_type</span><span class=\\"pi\\">:</span> <span class=\\"s\\">tech</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>那么之后每次创建文件时,在头部写<code class=\\"language-plaintext highlighter-rouge\\">category</code>一定要与这些<code class=\\"language-plaintext highlighter-rouge\\">categories</code>中的<code class=\\"language-plaintext highlighter-rouge\\">html</code>文件对应起来。</p>\\n\\n<h4 id=\\"q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</h4>\\n\\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">*</span> TOC\\n{:toc}\\n</code></pre></div></div>\\n\\n<h4 id=\\"q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\\n\\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\\n\\n<h5 id=\\"在-githubio-上\\">在 GitHub.io 上</h5>\\n\\n<p>先修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">math_engine</span><span class=\\"pi\\">:</span> <span class=\\"s\\">katex</span>\\n</code></pre></div></div>\\n\\n<p>然后修改 <code class=\\"language-plaintext highlighter-rouge\\">_includes/head.html</code> 文件,在 <code class=\\"language-plaintext highlighter-rouge\\">&lt;head&gt;</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">&lt;/head&gt;</code> 中间:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\">&lt;!--KaTeX--&gt;</span>\\n <span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span>\\n <span class=\\"na\\">href=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script&gt;</span>\\n <span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">addEventListener</span><span class=\\"p\\">(</span><span class=\\"dl\\">\\"</span><span class=\\"s2\\">DOMContentLoaded</span><span class=\\"dl\\">\\"</span><span class=\\"p\\">,</span> <span class=\\"kd\\">function</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"nx\\">renderMathInElement</span><span class=\\"p\\">(</span><span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">body</span><span class=\\"p\\">,</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// ...options...</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"nt\\">&lt;/script&gt;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</h5>\\n\\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem <span class=\\"nb\\">install </span>kramdom-math-katex\\n\\ngem <span class=\\"nb\\">install </span>katex\\ngem <span class=\\"nb\\">install </span>execjs\\n\\ngem <span class=\\"nb\\">install </span>therubyracer\\ngem <span class=\\"nb\\">install </span>therubyrhino\\ngem <span class=\\"nb\\">install </span>duktape\\n</code></pre></div></div>\\n\\n<h5 id=\\"使用示例\\">使用示例</h5>\\n\\n<p>以如下方式输入输入如下内容:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}\\n$$ \\\\sum_{i=1}^{n} a_i $$\\n{% endraw %}\\n</code></pre></div></div>\\n\\n<p>就会得到一个数学公式:</p>\\n\\n\\\\[\\\\sum_{i=1}^{n} a_i\\\\]\\n\\n<h4 id=\\"q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\\n\\n<p>这要依赖 <code class=\\"language-plaintext highlighter-rouge\\">jekyll-graphviz-dot</code>,修改 <code class=\\"language-plaintext highlighter-rouge\\">Gemfile</code> 增加一句:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>group :jekyll_plugins <span class=\\"k\\">do\\n </span>gem <span class=\\"s2\\">\\"jekyll-graphviz-dot\\"</span>\\nend\\n</code></pre></div></div>\\n\\n<p>再修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 配置文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-graphviz</span>\\n</code></pre></div></div>\\n\\n<p>再在本地安装 graphviz,可以通过 <code class=\\"language-plaintext highlighter-rouge\\">conda install graphviz</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">brew install graphviz</code>。然后 <code class=\\"language-plaintext highlighter-rouge\\">bundle install</code> 再 <code class=\\"language-plaintext highlighter-rouge\\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\\n\\n<pre><code class=\\"language-graphviz\\">{% graph some graph title %}\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n{% endgraph %}\\n</code></pre>\\n\\n<p>如果看到如下效果,就说明你都配置成功了:</p>\\n\\n<div class=\\"graphviz-wrapper\\">\\n\\n<!-- Generated by graphviz version 2.43.0 (0)\\n -->\\n<!-- Title: G Pages: 1 -->\\n<svg role=\\"img\\" aria-label=\\"some graph title\\" width=\\"89pt\\" height=\\"188pt\\" viewBox=\\"0.00 0.00 89.00 188.00\\">\\n<title>some graph title</title>\\n<desc>\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n</desc>\\n\\n<g id=\\"graph0\\" class=\\"graph\\" transform=\\"scale(1 1) rotate(0) translate(4 184)\\">\\n<title>G</title>\\n<polygon fill=\\"white\\" stroke=\\"transparent\\" points=\\"-4,4 -4,-184 85,-184 85,4 -4,4\\" />\\n<!-- a -->\\n<g id=\\"node1\\" class=\\"node\\">\\n<title>a</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-162\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-158.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">a</text>\\n</g>\\n<!-- b -->\\n<g id=\\"node2\\" class=\\"node\\">\\n<title>b</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"27\\" cy=\\"-90\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"27\\" y=\\"-86.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">b</text>\\n</g>\\n<!-- a&#45;&gt;b -->\\n<g id=\\"edge1\\" class=\\"edge\\">\\n<title>a&#45;&gt;b</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\\" />\\n</g>\\n<!-- c -->\\n<g id=\\"node3\\" class=\\"node\\">\\n<title>c</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-18\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-14.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">c</text>\\n</g>\\n<!-- b&#45;&gt;c -->\\n<g id=\\"edge2\\" class=\\"edge\\">\\n<title>b&#45;&gt;c</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\\" />\\n</g>\\n<!-- c&#45;&gt;a -->\\n<g id=\\"edge3\\" class=\\"edge\\">\\n<title>c&#45;&gt;a</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\\" />\\n</g>\\n</g>\\n</svg>\\n</div>\\n\\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\\n\\n<h4 id=\\"q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</h4>\\n\\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 呢?方法如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}{%{% endraw %} raw %}\\n{% raw %}{%{% endraw %} endraw %}\\n</code></pre></div></div>\\n\\n<p>如上,就是用 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 把 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 包起来,但是 <code class=\\"language-plaintext highlighter-rouge\\">%}</code> 不用包。应该讲的很清楚了吧。</p>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<ol>\\n <li><a href=\\"https://bundler.io\\">https://bundler.io</a></li>\\n <li><a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></li>\\n <li><a href=\\"https://zhuanlan.zhihu.com/p/87225594\\">https://zhuanlan.zhihu.com/p/87225594</a></li>\\n <li><a href=\\"https://chat.openai.com/chat\\">https://chat.openai.com/chat</a></li>\\n <li><a href=\\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\\n <li><a href=\\"https://github.com/dyutibarma/monochrome\\">https://github.com/dyutibarma/monochrome</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\\n <li><a href=\\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\\n <li><a href=\\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</title>\\n \\t<meta name=\\"description\\" content=\\"阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</h2>\\t\\t\\n\\t<time datetime=\\"2021-11-11T19:59:43+00:00\\" class=\\"by-line\\">11 Nov 2021, 杭州 | 作者 麦克船长 | 总计 917 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2021-11-11-captain-tttm-1.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h3 id=\\"天天特卖团队理念\\">天天特卖团队理念</h3>\\n\\n<h4 id=\\"特卖合伙人\\">特卖合伙人</h4>\\n\\n<p>以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。特卖核心管理团队每 Q 会进行一次班子建设通晒。</p>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-9.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h4 id=\\"如何理解协作\\">如何理解协作?</h4>\\n\\n<p>从长时间线来看,我们是为了不断积累信用,像一张信用卡一样,不断获得别人愿意支持我们的更大额度。不要事情结果还可以,而我们却没有积累到信用。互联网本质也是现代工业。而现代工业,一是社会分工,二是社会协作。想取得现代工业项目的结果,就要有更大的人才包容度、环境包容度。工作的结果就是在妥协与博弈中取得的,这是和光同尘的本质,也是现代工业复杂系统拿到结果的本质。只有这样我们才能让越来越多的人追随我们一起 do something,这种追随不一定只有上下级才是,而是愿意并且相信和我们能到达更远的地方。这背后的信用,要我们一步一个脚印地去积累,对他人给予的信任要保持敬畏、如履薄冰、懂得感恩。对每一段阶段性或长或短结束的合作,都要表达感谢。</p>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-8.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h4 id=\\"如何看待同学的优势及短板\\">如何看待同学的优势及短板?</h4>\\n\\n<ul>\\n <li>优势:讲优势有两个可能的目的,要么组织会在未来任务分配上重点考虑发挥该同学优势的事情,要告诉 TA,要激励 TA,是 TA 前行的自信来源之一。要么是对于同学也把握不准的特点,我们明确告诉 TA 这是你被我欣赏的优点。</li>\\n <li>短板:什么是要讲的短板?未来一段时间,最期待你补足提升的。一旦这方面显著进步,就会向上迈进很大一步,甚至可以突破自己当下成长的瓶颈。要花多少篇幅讲?要比优势,有更大篇幅去讲。讲完就结束了么?对这个短板,一定要表达态度,也一定要对是否有方法、什么方法来补足短板要和同学沟通。</li>\\n <li>无论是优势,还是短板,要说到点儿上,不要说片儿汤话。要让同学们能够引起思考、启发的。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-10.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h3 id=\\"天天特卖期待你的加入\\">天天特卖期待你的加入!</h3>\\n\\n<p>新天天特卖缘起于「手淘下沉市场战役)」,于 2021 年初上线,以「极致性价比货源、裸价直降、全网比价、买贵必赔」打造手淘极致价格敏感人群的购物阵地。目前天天特卖团队有行业运营、用户运营、数据策略、整合营销、直播运营、内容运营等岗位,有兴趣的同学可以钉钉随时找我,期待你的加入!</p>\\n\\n<h4 id=\\"欢迎添加我的微信sinosuperman-推荐自荐--\\">欢迎添加我的微信:sinosuperman 推荐、自荐 ^ ^</h4>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-11.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-2.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-3.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-4.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-5.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-6.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-7.jpg\\" alt=\\"imagee\\" /></p>\\n\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的阿里一年香(入职阿里一周年)</title>\\n \\t<meta name=\\"description\\" content=\\"本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的阿里一年香(入职阿里一周年)</h2>\\t\\t\\n\\t<time datetime=\\"2021-06-04T15:42:43+00:00\\" class=\\"by-line\\">04 Jun 2021, 杭州 | 作者 麦克船长 | 总计 304 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>To 钟超</p>\\n\\n<p>1 周年快乐!很开心我们有这样一段共事的机会,虽开始时有些许波折,但随着进一步相处,我们很快能做到彼此欣赏、英雄相惜、默契配合,也特别感谢你对我的信任和支持,这是一切共事的基础。你强大的自驱力、脑力、对新事物的理解学习能力,都是最近几手新人里比较突出的。特别钦佩于你的执着和初性,对一件事认定后,迸发出的强大战斗力和决心。今天特卖这个新业务需要扎下根基,还真的需要一些舍我其谁的胆魄和更为犀利的突破,我也相信「新特卖」能成为你在阿里又一代表作,我希望我们的团队能为之骄傲和自豪,我们能不负公司所托,真正在下沉市场这场硬仗上有所建树,井取得令我们自己感到骄傲的突破,一起加油。</p>\\n\\n<p>From 麦克船长的主管</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>担任淘宝产品总负责人的双十一,是怎样的体验?</title>\\n \\t<meta name=\\"description\\" content=\\"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>担任淘宝产品总负责人的双十一,是怎样的体验?</h2>\\t\\t\\n\\t<time datetime=\\"2020-11-11T15:59:43+00:00\\" class=\\"by-line\\">11 Nov 2020, 杭州 | 作者 麦克船长 | 总计 138 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\\n\\n<p><img src=\\"/img/src/2020-11-11-captain-double-eleven-1.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-2.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-3.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-4.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-5.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-6.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-7.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-8.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-9.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-10.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-11.jpg\\" alt=\\"image\\" /></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>疫后怎么做餐饮品牌?三叉戟模式或成标配</title>\\n \\t<meta name=\\"description\\" content=\\"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>疫后怎么做餐饮品牌?三叉戟模式或成标配</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-14T16:42:43+00:00\\" class=\\"by-line\\">14 Apr 2020, 杭州 | 作者 麦克船长 | 总计 1845 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\\n\\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\\n\\n<ul>\\n <li>外卖</li>\\n <li>做饭</li>\\n <li>速食</li>\\n</ul>\\n\\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\\n\\n<h3 id=\\"标品化外卖业务\\">标品化外卖业务</h3>\\n\\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\\n\\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\\n\\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\\n\\n<ul>\\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\\n</ul>\\n\\n<h3 id=\\"社区生鲜前置仓\\">社区生鲜前置仓</h3>\\n\\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\\n\\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\\n\\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\\n\\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\\n\\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\\n\\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\\n</ul>\\n\\n<h3 id=\\"线上预包装食品\\">线上预包装食品</h3>\\n\\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\\n\\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\\n\\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\\n</ul>\\n\\n<h3 id=\\"线下重构新餐饮时代到来\\">线下重构,新餐饮时代到来</h3>\\n\\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\\n\\n<ul>\\n <li>用「标品化外卖」覆盖外卖场景</li>\\n <li>用「生鲜前置仓」覆盖做饭场景</li>\\n <li>用「预包装食品」覆盖速食场景</li>\\n</ul>\\n\\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\\n\\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\\n\\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>延迟满足,才有自由</title>\\n \\t<meta name=\\"description\\" content=\\"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>延迟满足,才有自由</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-11T06:18:03+00:00\\" class=\\"by-line\\">11 Apr 2020, 杭州 | 作者 麦克船长 | 总计 4478 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\\n\\n<h3 id=\\"1儿童时期的延迟满足\\">1、儿童时期的延迟满足</h3>\\n\\n<h4 id=\\"棉花糖实验\\">棉花糖实验</h4>\\n\\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\\n\\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\\n\\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\\n\\n<p>先讲两个我自己的例子吧。</p>\\n\\n<h4 id=\\"黑加仑零食\\">黑加仑零食</h4>\\n\\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\\n\\n<h4 id=\\"暑假作业\\">暑假作业</h4>\\n\\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\\n\\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\\n\\n<h4 id=\\"延迟满足的背后是意志力自控力\\">延迟满足的背后,是意志力/自控力</h4>\\n\\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\\n\\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\\n\\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\\n\\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\\n\\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\\n\\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\\n\\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\\n\\n<h3 id=\\"2快乐理论挑战延迟满足\\">2、快乐理论,挑战延迟满足</h3>\\n\\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\\n\\n<h4 id=\\"烂苹果\\">烂苹果</h4>\\n\\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\\n\\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\\n\\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\\n\\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\\n\\n<h4 id=\\"快乐理论\\">快乐理论</h4>\\n\\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\\n\\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\\n\\n<p>但这个理论角度对吗?</p>\\n\\n<h3 id=\\"3我们应该在哪个范畴内讨论延迟满足\\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\\n\\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\\n\\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\\n\\n<h4 id=\\"仅有正反馈刺激的奶头乐\\">仅有正反馈刺激的奶头乐</h4>\\n\\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\\n\\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\\n\\n<h4 id=\\"延迟满足重在顺序而非时间\\">延迟满足重在顺序,而非时间</h4>\\n\\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\\n\\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\\n\\n<h3 id=\\"4常见的延迟满足与即时满足\\">4、常见的延迟满足与即时满足</h3>\\n\\n<h4 id=\\"初阶对手vs浅度延迟满足\\">初阶对手 vs 浅度延迟满足</h4>\\n\\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\\n\\n<ul>\\n <li>\\n <p>睡懒觉</p>\\n </li>\\n <li>\\n <p>过量饮食</p>\\n </li>\\n <li>\\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\\n </li>\\n <li>\\n <p>冲动情绪</p>\\n </li>\\n <li>\\n <p>炫耀(粗俗说就是装B)</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\\n\\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\\n\\n<h4 id=\\"中阶对手vs中度延迟满足\\">中阶对手 vs 中度延迟满足</h4>\\n\\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\\n\\n<ul>\\n <li>\\n <p>在家边看电视/视频,边学习/工作</p>\\n </li>\\n <li>\\n <p>依赖二手知识,而怠于一手知识</p>\\n </li>\\n <li>\\n <p>刷知乎、行业资讯 APP</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>例子会有很多,我们仅稍微说下这三个。</p>\\n\\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\\n\\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\\n\\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\\n\\n<h4 id=\\"高阶对手vs深度延迟满足\\">高阶对手 vs 深度延迟满足</h4>\\n\\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\\n\\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\\n\\n<p>我这里就说一个影响很大的:</p>\\n\\n<p>* 急于行动</p>\\n\\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\\n\\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\\n\\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\\n\\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\\n\\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\\n\\n<h3 id=\\"5延迟满足的驻留时长\\">5、延迟满足的驻留时长</h3>\\n\\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\\n\\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\\n\\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\\n\\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\\n\\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\\n\\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\\n\\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\\n\\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>未来人工智能就是要:让普通人过上现在富豪们的生活</title>\\n \\t<meta name=\\"description\\" content=\\"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>未来人工智能就是要:让普通人过上现在富豪们的生活</h2>\\t\\t\\n\\t<time datetime=\\"2017-02-23T18:23:33+00:00\\" class=\\"by-line\\">23 Feb 2017, 北京 | 作者 麦克船长 | 总计 627 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\\n\\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\\n\\n<h4 id=\\"但是我没有钱呢\\">但是我没有钱呢?</h4>\\n\\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\\n\\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\\n\\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\\n\\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\\n\\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\\n\\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\\n\\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>我们是应该「断舍离」还是「念念不忘,必有回响」</title>\\n \\t<meta name=\\"description\\" content=\\"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>我们是应该「断舍离」还是「念念不忘,必有回响」</h2>\\t\\t\\n\\t<time datetime=\\"2017-01-31T20:59:21+00:00\\" class=\\"by-line\\">31 Jan 2017, 北京 | 作者 麦克船长 | 总计 2577 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"引子\\">引子</h3>\\n\\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\\n\\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\\n\\n<p>「嗨,我说老朱,讲讲你啊!」</p>\\n\\n<p>「我就不提啦。」</p>\\n\\n<p>「为什么啊?说说,说说!」</p>\\n\\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\\n\\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\\n\\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\\n\\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\\n\\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\\n\\n<blockquote>\\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\\n</blockquote>\\n\\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\\n\\n<h3 id=\\"断舍离\\">断舍离</h3>\\n\\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\\" alt=\\"image\\" /></p>\\n\\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\\n\\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\\n\\n<h3 id=\\"念念不忘必有回响\\">念念不忘,必有回响</h3>\\n\\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\\" alt=\\"image\\" /></p>\\n\\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\\n\\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\\n\\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\\n\\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\\n\\n<h3 id=\\"用正负反馈来判断何时何为\\">用「正负反馈」来判断何时何为?</h3>\\n\\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\\n\\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\\n\\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\\n\\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\\n\\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\\n\\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\\n\\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\\n\\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\\n\\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\\n\\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\\n\\n<p>然后,我们一起等待,那声回响。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-08-04T17:58:17+00:00\\" class=\\"by-line\\">04 Aug 2012, 广州 | 作者 麦克船长 | 总计 5236 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfpserver-线程的启动和等待\\" id=\\"markdown-toc-一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</a> <ul>\\n <li><a href=\\"#1pocothread\\" id=\\"markdown-toc-1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></a></li>\\n <li><a href=\\"#2封装一个可运行线程的类\\" id=\\"markdown-toc-2封装一个可运行线程的类\\">2、封装一个可运行线程的类</a></li>\\n <li><a href=\\"#3启动-rtmfpserver-线程\\" id=\\"markdown-toc-3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</a></li>\\n <li><a href=\\"#4rtmfpserver-线程等待\\" id=\\"markdown-toc-4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二rtmfpmanager-对-rtmfpserver-的影响\\" id=\\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</a></li>\\n</ul>\\n\\n<h3 id=\\"一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</h3>\\n\\n<h4 id=\\"1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></h4>\\n\\n<p>Cumulus 大量使用了 <code class=\\"language-plaintext highlighter-rouge\\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PoechantRunnable</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// your codes</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">};</span>\\n \\n<span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">PoechantRunnable</span> <span class=\\"n\\">runnable</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Image that it's a gift</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">thread</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// And… thread is just like your girl</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">runnable</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Okay, give your sweet babe the gift :)</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2封装一个可运行线程的类\\">2、封装一个可运行线程的类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中实现了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 类,该类继承了 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,就是上面那个 <code class=\\"language-plaintext highlighter-rouge\\">gift</code> 喽。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">StartableProcess</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span><span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">);</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>可以看到其中有 <code class=\\"language-plaintext highlighter-rouge\\">Startable&amp; _startable</code> 引用成员,它并没有继承 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,而是封装了 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">_thread</span><span class=\\"p\\">;</span>\\n<span class=\\"n\\">StartableProcess</span> <span class=\\"n\\">_process</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>这里 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 封装了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 成员,与 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\\n\\n<h4 id=\\"3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</h4>\\n<p>我们可以看到在 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的构造函数中初始化了 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\\"language-plaintext highlighter-rouge\\">(Flag Field)_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,因为它还没有调用启动函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_name</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_stop</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_process</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>初始化 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 时,调用 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">StartableProcess</span><span class=\\"o\\">::</span><span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">(</span><span class=\\"n\\">startable</span><span class=\\"p\\">){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>传入 <code class=\\"language-plaintext highlighter-rouge\\">_startable</code> 的引用。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的,然后通过调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 来启动,启动后会响应到 <code class=\\"language-plaintext highlighter-rouge\\">run()</code>。下面我们以 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程为例。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 类是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPServer</span>\\n <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Gateway</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">protected</span> <span class=\\"n\\">Handler</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">SocketHandler</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">RTMFPServer</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">cores</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">(</span><span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>所在线程</th>\\n <th>调用者</th>\\n <th>函数</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>主线程</td>\\n <td>main(…)</td>\\n <td> </td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer从Startable继承来的Thread成员</td>\\n <td>Thread::start(…)</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\\n <td>StartableProcess::run()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::run()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</h4>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"n\\">terminate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 函数很简单,如下只进行了 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">giveHandle()</code> 两个操作。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">){</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">()</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">giveHandle</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的,声明如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">WakeUpType</span> <span class=\\"nf\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>在运行状态下,<code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,而默认参数 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code> 为 0,所以会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>这个 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员是一个 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 对象,<code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::wait()</code> 后,会一直等待 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\\"language-plaintext highlighter-rouge\\">wait</code> 的状态。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">set</code> 的动作是由:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::requestHandle()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">PoolThread::push(Poco::AutoPtr&lt;RunnableType&gt;&amp; pRunnable)</code></li>\\n</ul>\\n\\n<p>执行的。</p>\\n\\n<h3 id=\\"二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 同样,继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPManager</span> <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Task</span><span class=\\"p\\">,</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span>\\n</code></pre></div></div>\\n\\n<p>在构造函数中将 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\\"language-plaintext highlighter-rouge\\">_server</code> 引用成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPManager</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">server</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_server</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Task</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPManager\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"cm\\">/* ...... */</span>\\n\\n<span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_server</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的构造函数中调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 成员函数,是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的线程。然后响应到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager::run()</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">PRIO_LOW</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">2000</span><span class=\\"p\\">)</span><span class=\\"o\\">!=</span><span class=\\"n\\">STOP</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">waitHandle</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里要强调的是,这里的 <code class=\\"language-plaintext highlighter-rouge\\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\\n\\n<p>在这里我们可以看到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的主循环的等待时间的。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>你可以自行修改 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 的参数,这样就会调用 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code>)来进行睡眠。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\\"language-plaintext highlighter-rouge\\">handle</code> 成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handle</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_server</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里就会调用到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 源码时知道 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code> 函数并不是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程内运行的,而是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">manage</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\\"language-plaintext highlighter-rouge\\">Session</code>。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</title>\\n \\t<meta name=\\"description\\" content=\\"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</h2>\\t\\t\\n\\t<time datetime=\\"2012-07-23T03:07:43+00:00\\" class=\\"by-line\\">23 Jul 2012, 广州 | 作者 麦克船长 | 总计 3111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1客户端发布publishing-on-client-side\\" id=\\"markdown-toc-1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</a></li>\\n <li><a href=\\"#2服务器端server-side\\" id=\\"markdown-toc-2服务器端server-side\\">2、服务器端(Server-side)</a></li>\\n <li><a href=\\"#3客户端订阅subscribing-on-client-side\\" id=\\"markdown-toc-3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</a></li>\\n <li><a href=\\"#4reference\\" id=\\"markdown-toc-4reference\\">4、Reference</a></li>\\n</ul>\\n\\n<p>整个流程概括如下:</p>\\n\\n<p>Flash 客户端通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 建立连接,然后通过 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\\n\\n<h3 id=\\"1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</h3>\\n\\n<p>通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\\"/2012/04/10/openrtmfp-cumulus-1/\\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\\"language-plaintext highlighter-rouge\\">nc</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost:1935\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\\"language-plaintext highlighter-rouge\\">ns1</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">ns1</span><span class=\\"p\\">.</span><span class=\\"nx\\">publish</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"poechant_media_flow\\"</span><span class=\\"p\\">,</span> <span class=\\"s2\\">\\"live\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\\n\\n<h3 id=\\"2服务器端server-side\\">2、服务器端(Server-side)</h3>\\n\\n<p>Cumulus 通过 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPReceiving</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">rtmfpReceiving</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\\n\\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\\"language-plaintext highlighter-rouge\\">Handshake</code> 类的 <code class=\\"language-plaintext highlighter-rouge\\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ServerSession</span><span class=\\"o\\">::</span><span class=\\"n\\">packetHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Flow</span><span class=\\"o\\">::</span><span class=\\"n\\">fragmentSortedHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">stage</span><span class=\\"p\\">,</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">fragment</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">flags</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF_WITH_HANDLER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">messageHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">amf</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AUDIO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">audioHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">VIDEO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">videoHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">rawHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>接下来在 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushAudioPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushVideoPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushDataPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中的 <code class=\\"language-plaintext highlighter-rouge\\">_listeners</code> 就是该 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Listener</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">addListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">writer</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">unbuffered</span><span class=\\"p\\">);</span>\\n \\n<span class=\\"kt\\">void</span> <span class=\\"nf\\">removeListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这两个函数来实现的。</p>\\n\\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\\n\\n<h3 id=\\"3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</h3>\\n\\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>ns2.play(\\"poechant_media_flow\\");\\n</code></pre></div></div>\\n\\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\\"language-plaintext highlighter-rouge\\">NetStream::send(…)</code> 的,用于发送 <code class=\\"language-plaintext highlighter-rouge\\">Data</code>,<code class=\\"language-plaintext highlighter-rouge\\">Audio</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Video</code> 的程序可以参考该例修改。</p>\\n\\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 初始化的时候,传入 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\\n\\n<h3 id=\\"4reference\\">4、Reference</h3>\\n\\n<ul>\\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\\n</ul>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-25T02:56:26+00:00\\" class=\\"by-line\\">25 Jun 2012, 广州 | 作者 麦克船长 | 总计 2111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中的线程都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>,在其中封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 函数如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样一个类继承 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code>,然后调用到该类自己的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\\"language-plaintext highlighter-rouge\\">SocketManager</code> 为例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">SocketManager</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span> \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>我们要看看这个 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 是怎么回事,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">running</span><span class=\\"p\\">()</span> <span class=\\"k\\">const</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>很简单,就是通过 <code class=\\"language-plaintext highlighter-rouge\\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 是什么时候被设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 的呢?就是上面的 <code class=\\"language-plaintext highlighter-rouge\\">start()</code>,这里存在的一个问题就是先 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 线程,再设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_thread.start(_process);\\n_stop=false;\\n</code></pre></div></div>\\n\\n<p>而 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 之后 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 的时候就开始通过 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 来判断 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值了。所以你会在使用 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>它们是:</p>\\n\\n<ul>\\n <li>主线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程</li>\\n</ul>\\n\\n<p>而异常情况可能是 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动,甚至 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\\" alt=\\"image\\" /></p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动的情况 T.T</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\\n\\n<p>解决办法就是将 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span> \\n <span class=\\"c1\\">// June 25th, 2012, Michael@YY</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-07T15:34:18+00:00\\" class=\\"by-line\\">07 Jun 2012, 广州 | 作者 麦克船长 | 总计 1538 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>OpenRTMFP/Cumulus 提供了 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>。</p>\\n\\n<p>一般来说,Thread A 会准备好要 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息。</p>\\n\\n<p>但是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>由于在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">createAMFMessage</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n \\n <span class=\\"c1\\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"p\\">(</span><span class=\\"n\\">_closed</span> <span class=\\"o\\">||</span> <span class=\\"n\\">signature</span><span class=\\"p\\">.</span><span class=\\"n\\">empty</span><span class=\\"p\\">()</span> <span class=\\"o\\">||</span> <span class=\\"n\\">_band</span><span class=\\"p\\">.</span><span class=\\"n\\">failed</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">MessageBuffered</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"n\\">_MessageNull</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后再调用时最后再增加 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Mutex</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedLock</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">msgQueueMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就基本解决了这个线程安全问题。</p>\\n\\n<p>另外,使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>一名出色软件工程师的技术基本功:编程与工具</title>\\n \\t<meta name=\\"description\\" content=\\"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>一名出色软件工程师的技术基本功:编程与工具</h2>\\t\\t\\n\\t<time datetime=\\"2012-05-15T17:06:59+00:00\\" class=\\"by-line\\">15 May 2012, 广州 | 作者 麦克船长 | 总计 2132 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"0写在前面\\">0、写在前面</h3>\\n\\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\\n\\n<h3 id=\\"1编程\\">1、编程</h3>\\n\\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\\n\\n<h4 id=\\"如果你精通c语言\\">如果你精通 C 语言</h4>\\n\\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\\n\\n<h4 id=\\"如果你精通c语言-1\\">如果你精通 C++ 语言</h4>\\n\\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\\n\\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\\n\\n<h4 id=\\"精通的关键还是针对语言核心来说的\\">精通的关键,还是针对语言核心来说的。</h4>\\n\\n<p>第一,你要对这个语言的语法特性熟稔;</p>\\n\\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\\n\\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\\n\\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\\n\\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\\n\\n<h4 id=\\"与计算机网络的基础结构相关联的技术实现\\">与计算机、网络的基础结构相关联的技术实现</h4>\\n\\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\\n\\n<h3 id=\\"2工具\\">2、工具</h3>\\n\\n<p>对于工具有三个层面:</p>\\n\\n<p>第一,是熟练的使用一些工具。</p>\\n\\n<p>第二,是能够发现提高生产力的工具。</p>\\n\\n<p>第三,是能够在无可用工具时自己编写工具。</p>\\n\\n<p>那么都有哪些最最最基本的工具呢?</p>\\n\\n<h4 id=\\"ideintegrateddevelopmentenvironment\\">IDE(Integrated Development Environment)</h4>\\n\\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\\n\\n<h4 id=\\"vcsversioncontrolsystem\\">VCS(Version Control System)</h4>\\n\\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\\n\\n<blockquote>\\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black &amp; white.</p>\\n</blockquote>\\n\\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\\n\\n<h4 id=\\"clicommandlineinterface\\">CLI(Command Line Interface)</h4>\\n\\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\\n\\n<h3 id=\\"3结语\\">3、结语</h3>\\n\\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T03:31:10+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 12668 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一流缓冲区\\" id=\\"markdown-toc-一流缓冲区\\">一、流缓冲区</a> <ul>\\n <li><a href=\\"#1了解-stdstreambuf\\" id=\\"markdown-toc-1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></a> <ul>\\n <li><a href=\\"#11单步移动内置指针\\" id=\\"markdown-toc-11单步移动内置指针\\">1.1、单步移动内置指针</a></li>\\n <li><a href=\\"#12获取-get-指针和-put-指针的位置\\" id=\\"markdown-toc-12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</a></li>\\n <li><a href=\\"#13设置-get-和-put-指针可达区域的上下界\\" id=\\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2memorystreambuf\\" id=\\"markdown-toc-2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></a> <ul>\\n <li><a href=\\"#21移动内置的-get-和-put-指针\\" id=\\"markdown-toc-21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</a></li>\\n <li><a href=\\"#22获取-get-和-put-指针当前位置\\" id=\\"markdown-toc-22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</a></li>\\n <li><a href=\\"#23获取缓冲区的起始位置和大小\\" id=\\"markdown-toc-23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</a></li>\\n <li><a href=\\"#24缓冲区的已写字节数\\" id=\\"markdown-toc-24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</a></li>\\n <li><a href=\\"#25显式设定-put-和-get-指针位置\\" id=\\"markdown-toc-25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</a></li>\\n <li><a href=\\"#26-修改缓冲区大小\\" id=\\"markdown-toc-26-修改缓冲区大小\\">2.6 修改缓冲区大小</a></li>\\n <li><a href=\\"#27构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二io-流\\" id=\\"markdown-toc-二io-流\\">二、IO 流</a> <ul>\\n <li><a href=\\"#1了解-stdios\\" id=\\"markdown-toc-1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></a></li>\\n <li><a href=\\"#2memoryios\\" id=\\"markdown-toc-2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></a> <ul>\\n <li><a href=\\"#21构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#22得到-memorystreambuf-成员的地址\\" id=\\"markdown-toc-22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</a></li>\\n <li><a href=\\"#23当前位置\\" id=\\"markdown-toc-23当前位置\\">2.3、当前位置</a></li>\\n <li><a href=\\"#24封装-memorystreambuf-成员的一些函数\\" id=\\"markdown-toc-24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</a></li>\\n <li><a href=\\"#25-缓冲区可读数据的字节数\\" id=\\"markdown-toc-25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3输入流\\" id=\\"markdown-toc-3输入流\\">3、输入流</a></li>\\n <li><a href=\\"#4输出流\\" id=\\"markdown-toc-4输出流\\">4、输出流</a> <ul>\\n <li><a href=\\"#41-构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#42-读取和设定已写字节数\\" id=\\"markdown-toc-42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</a></li>\\n <li><a href=\\"#43-当前位置\\" id=\\"markdown-toc-43-当前位置\\">4.3 当前位置</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#三局部内存片\\" id=\\"markdown-toc-三局部内存片\\">三、局部内存片</a> <ul>\\n <li><a href=\\"#1构造函数\\" id=\\"markdown-toc-1构造函数\\">1、构造函数</a></li>\\n <li><a href=\\"#2析构函数\\" id=\\"markdown-toc-2析构函数\\">2、析构函数</a></li>\\n <li><a href=\\"#3缓冲区切割\\" id=\\"markdown-toc-3缓冲区切割\\">3、缓冲区切割</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\\n\\n<h3 id=\\"一流缓冲区\\">一、流缓冲区</h3>\\n\\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\\n\\n<h4 id=\\"1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></h4>\\n\\n<p>首先要了解 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 内置了一个 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针和一个 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针。<code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\\"language-plaintext highlighter-rouge\\">g</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">p</code> 就分别表示 get pointer 和 put pointer。</p>\\n\\n<h5 id=\\"11单步移动内置指针\\">1.1、单步移动内置指针</h5>\\n\\n<p>Increase get pointer: Advances the get pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">gbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>Increase put pointer: Advances the put pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">pbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</h5>\\n\\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">gptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">pptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</h5>\\n\\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setg</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gnext</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setp</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<h4 id=\\"2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></h4>\\n\\n<p>类定义:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryStreamBuf</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">streambuf</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">friend</span> <span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span><span class=\\"p\\">;</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">overflow</span><span class=\\"p\\">(</span><span class=\\"n\\">int_type</span> <span class=\\"n\\">c</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">underflow</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">sync</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"k\\">operator</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\\n\\n<h5 id=\\"21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针都移动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">gbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</h5>\\n\\n<p>封装 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">gptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</h5>\\n\\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</h5>\\n\\n<p>读取(其中也可能发生设置操作):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// 已写字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">written</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_written</span><span class=\\"o\\">=</span><span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</h5>\\n\\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Save nb char written</span>\\n \\n <span class=\\"c1\\">// 移动 put 指针</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 移动 get 指针</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"26-修改缓冲区大小\\">2.6 修改缓冲区大小</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// 大小标识</span>\\n <span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// gptr 当前位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span> \\n <span class=\\"c1\\">// pptr 当前位置</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>构造函数会设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">bufferSize</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数会拷贝对方的 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code>、<code class=\\"language-plaintext highlighter-rouge\\">bufferSizse</code>、<code class=\\"language-plaintext highlighter-rouge\\">_written</code>,并设定 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>、<code class=\\"language-plaintext highlighter-rouge\\">pptr</code>。注意设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 时,要分别调用 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pbump</code>,因为 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 仅将 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">(),</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">));</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二io-流\\">二、IO 流</h3>\\n\\n<h4 id=\\"1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></h4>\\n\\n<p>Initialize object [<code class=\\"language-plaintext highlighter-rouge\\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">init</span> <span class=\\"p\\">(</span> <span class=\\"n\\">streambuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">sb</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>初始化后如下函数的返回值:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>member function</th>\\n <th>value</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>rdbuf()</td>\\n <td>sb</td>\\n </tr>\\n <tr>\\n <td>tie()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>rdstate()</td>\\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\\n </tr>\\n <tr>\\n <td>exceptions()</td>\\n <td>goodbit</td>\\n </tr>\\n <tr>\\n <td>flags()</td>\\n <td>skipws | dec</td>\\n </tr>\\n <tr>\\n <td>width()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>precision()</td>\\n <td>6</td>\\n </tr>\\n <tr>\\n <td>fill()</td>\\n <td>‘ ’ (whitespace)</td>\\n </tr>\\n <tr>\\n <td>getloc()</td>\\n <td>a copy of locale()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code>,且是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryIOS</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"k\\">virtual</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ios</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span> <span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">poco_ios_init</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">init</code> 的宏定义,用于初始化成员 <code class=\\"language-plaintext highlighter-rouge\\">_buf</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_buf</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23当前位置\\">2.3、当前位置</h5>\\n\\n<p>这是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutpuStream</code> 继承时实现:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code> 封装为 <code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"p\\">(</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">());</span> <span class=\\"c1\\">// 缓冲区剩余可读数据字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">result</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3输入流\\">3、输入流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryInputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryInputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for reading.</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 以及 <code class=\\"language-plaintext highlighter-rouge\\">istream</code>。<code class=\\"language-plaintext highlighter-rouge\\">istream</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">iostream</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">basic_istream</code> 别名(<code class=\\"language-plaintext highlighter-rouge\\">typedef</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"k\\">const_cast</span><span class=\\"o\\">&lt;</span><span class=\\"kt\\">char</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>唯一的一个成员函数是 <code class=\\"language-plaintext highlighter-rouge\\">current</code>,封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的 <code class=\\"language-plaintext highlighter-rouge\\">gCurrent</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4输出流\\">4、输出流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryOutputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span>\\n <span class=\\"c1\\">/// An input stream for reading from a memory area.</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryOutputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for writing.</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>如下,不赘述了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</h5>\\n\\n<p>读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"43-当前位置\\">4.3 当前位置</h5>\\n\\n<p>与 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 中的封装类似:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三局部内存片\\">三、局部内存片</h3>\\n\\n<p>在第一部分的流缓冲区介绍 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 中有一个 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"1构造函数\\">1、构造函数</h4>\\n\\n<p>构造函数传入的参数对应的就是 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">(</span><span class=\\"n\\">offset</span><span class=\\"p\\">),</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">(</span><span class=\\"n\\">buffer</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&gt;=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2析构函数\\">2、析构函数</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3缓冲区切割\\">3、缓冲区切割</h4>\\n\\n<p>可以看到构造函数和析构函数中都调用了 <code class=\\"language-plaintext highlighter-rouge\\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\\n\\n<ul>\\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 获取到 gptr</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gpos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"c1\\">// 偏移缓冲区地址,并修改缓冲区大小</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">ppos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">gpos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">ppos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T02:04:55+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 30820 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一amf-数据类型定义\\" id=\\"markdown-toc-一amf-数据类型定义\\">一、AMF 数据类型定义</a> <ul>\\n <li><a href=\\"#1数据类型\\" id=\\"markdown-toc-1数据类型\\">1、数据类型</a></li>\\n <li><a href=\\"#2undefined-type\\" id=\\"markdown-toc-2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</a></li>\\n <li><a href=\\"#3null-type\\" id=\\"markdown-toc-3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</a></li>\\n <li><a href=\\"#4false-type\\" id=\\"markdown-toc-4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</a></li>\\n <li><a href=\\"#5true-type\\" id=\\"markdown-toc-5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</a></li>\\n <li><a href=\\"#6integer-type\\" id=\\"markdown-toc-6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</a></li>\\n <li><a href=\\"#7double-type\\" id=\\"markdown-toc-7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</a></li>\\n <li><a href=\\"#8string-type\\" id=\\"markdown-toc-8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</a></li>\\n <li><a href=\\"#9xmldocument-type\\" id=\\"markdown-toc-9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</a></li>\\n <li><a href=\\"#10date-type\\" id=\\"markdown-toc-10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</a></li>\\n <li><a href=\\"#11array-type\\" id=\\"markdown-toc-11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</a></li>\\n <li><a href=\\"#12object-type\\" id=\\"markdown-toc-12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</a></li>\\n <li><a href=\\"#13xml-type\\" id=\\"markdown-toc-13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</a></li>\\n <li><a href=\\"#14bytearray-type\\" id=\\"markdown-toc-14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</a></li>\\n <li><a href=\\"#15amf3-的使用\\" id=\\"markdown-toc-15amf3-的使用\\">15、AMF3 的使用</a> <ul>\\n <li><a href=\\"#151netconnection-and-amf-3\\" id=\\"markdown-toc-151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</a></li>\\n <li><a href=\\"#152netconnection-in-actionscript-30\\" id=\\"markdown-toc-152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</a></li>\\n <li><a href=\\"#153bytearray-idatainput-and-idataoutput\\" id=\\"markdown-toc-153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二binaryreaderwriter\\" id=\\"markdown-toc-二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></a> <ul>\\n <li><a href=\\"#1amf3-数据格式基础\\" id=\\"markdown-toc-1amf3-数据格式基础\\">1、AMF3 数据格式基础</a></li>\\n <li><a href=\\"#2序列化\\" id=\\"markdown-toc-2序列化\\">2、序列化</a></li>\\n <li><a href=\\"#3反序列化\\" id=\\"markdown-toc-3反序列化\\">3、反序列化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三packetreaderwriter\\" id=\\"markdown-toc-三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></a> <ul>\\n <li><a href=\\"#1packetreader\\" id=\\"markdown-toc-1packetreader\\">1、PacketReader</a> <ul>\\n <li><a href=\\"#11封装-memoryinputstream\\" id=\\"markdown-toc-11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></a></li>\\n <li><a href=\\"#12收缩缓冲区\\" id=\\"markdown-toc-12收缩缓冲区\\">1.2、收缩缓冲区</a></li>\\n <li><a href=\\"#13构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-13构造函数拷贝构造函数和析构函数\\">1.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2packetwriter\\" id=\\"markdown-toc-2packetwriter\\">2、<code class=\\"language-plaintext highlighter-rouge\\">PacketWriter</code></a> <ul>\\n <li><a href=\\"#21封装memoryoutputstream\\" id=\\"markdown-toc-21封装memoryoutputstream\\">2.1、封装<code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code></a></li>\\n <li><a href=\\"#22封装-binarywriter\\" id=\\"markdown-toc-22封装-binarywriter\\">2.2、封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code></a></li>\\n <li><a href=\\"#23构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-23构造函数拷贝构造函数和析构函数\\">2.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四amfreader\\" id=\\"markdown-toc-四amfreader\\">四、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code></a> <ul>\\n <li><a href=\\"#1objectdef\\" id=\\"markdown-toc-1objectdef\\">1、<code class=\\"language-plaintext highlighter-rouge\\">ObjectDef</code></a></li>\\n <li><a href=\\"#2amfreader-定义\\" id=\\"markdown-toc-2amfreader-定义\\">2、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 定义</a> <ul>\\n <li><a href=\\"#21构造函数析构函数\\" id=\\"markdown-toc-21构造函数析构函数\\">2.1、构造函数、析构函数</a></li>\\n <li><a href=\\"#22简单封装-packetreader-的一些函数\\" id=\\"markdown-toc-22简单封装-packetreader-的一些函数\\">2.2、简单封装 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 的一些函数</a></li>\\n <li><a href=\\"#23设置-gptr-位置\\" id=\\"markdown-toc-23设置-gptr-位置\\">2.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 位置</a></li>\\n <li><a href=\\"#24判断类型\\" id=\\"markdown-toc-24判断类型\\">2.4、判断类型</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3解析-as3-null\\" id=\\"markdown-toc-3解析-as3-null\\">3、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Null</code></a></li>\\n <li><a href=\\"#4解析-as3-number\\" id=\\"markdown-toc-4解析-as3-number\\">4、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Number</code></a></li>\\n <li><a href=\\"#5解析-as3-integer\\" id=\\"markdown-toc-5解析-as3-integer\\">5、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code></a></li>\\n <li><a href=\\"#6解析-as3-boolean\\" id=\\"markdown-toc-6解析-as3-boolean\\">6、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code></a></li>\\n <li><a href=\\"#7开始引用与结束引用\\" id=\\"markdown-toc-7开始引用与结束引用\\">7、开始引用与结束引用</a></li>\\n <li><a href=\\"#8解析-as3-bytearray\\" id=\\"markdown-toc-8解析-as3-bytearray\\">8、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code></a></li>\\n <li><a href=\\"#9解析-as3-date\\" id=\\"markdown-toc-9解析-as3-date\\">9、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Date</code></a></li>\\n <li><a href=\\"#10解析-as3-dictionary\\" id=\\"markdown-toc-10解析-as3-dictionary\\">10、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Dictionary</code></a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。</p>\\n\\n<h3 id=\\"一amf-数据类型定义\\">一、AMF 数据类型定义</h3>\\n\\n<h4 id=\\"1数据类型\\">1、数据类型</h4>\\n\\n<p>各种数据类型的标示都在 AMF.h 中定义为宏</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cp\\">#define AMF_NUMBER 0x00 // 浮点数\\n#define AMF_BOOLEAN 0x01 // 布尔型\\n#define AMF_STRING 0x02 // 字符串\\n#define AMF_BEGIN_OBJECT 0x03 // 对象,开始\\n#define AMF_NULL 0x05 // null\\n#define AMF_UNDEFINED 0x06\\n#define AMF_REFERENCE 0x07\\n#define AMF_MIXED_ARRAY 0x08\\n#define AMF_END_OBJECT 0x09 // 对象,结束\\n#define AMF_BEGIN_TYPED_OBJECT 0x10\\n#define AMF_STRICT_ARRAY 0x0A\\n#define AMF_DATE 0x0B // 日期\\n#define AMF_LONG_STRING 0x0C // 字符串\\n#define AMF_UNSUPPORTED 0x0D\\n</span> \\n<span class=\\"cp\\">#define AMF_AVMPLUS_OBJECT 0x11\\n#define AMF_END 0xFF\\n</span> \\n<span class=\\"cp\\">#define AMF3_UNDEFINED 0x00\\n#define AMF3_NULL 0x01\\n#define AMF3_FALSE 0x02\\n#define AMF3_TRUE 0x03\\n#define AMF3_INTEGER 0x04\\n#define AMF3_NUMBER 0x05\\n#define AMF3_STRING 0x06\\n#define AMF3_DATE 0x08\\n#define AMF3_ARRAY 0x09\\n#define AMF3_OBJECT 0x0A\\n#define AMF3_BYTEARRAY 0x0C\\n#define AMF3_DICTIONARY 0x11\\n</span></code></pre></div></div>\\n\\n<p>并定义了一个枚举类表示数据类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">AMF</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"k\\">enum</span> <span class=\\"n\\">Type</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Null</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Boolean</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Integer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Number</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">String</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Date</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Array</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Object</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ByteArray</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Dictionary</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RawObjectContent</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">End</span>\\n <span class=\\"p\\">};</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型标记表示,用于编码布尔值 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</h4>\\n\\n<p>true 类型由 true 类型标记表示,用于编码布尔值 true。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</h4>\\n\\n<p>在 AMF 3 中,整数使用可变长度的无符号 29 位整数进行序列化。ActionScript 3.0 中的整数类型 - 有符号 <code class=\\"language-plaintext highlighter-rouge\\">int</code> 类型和无符号 <code class=\\"language-plaintext highlighter-rouge\\">uint</code> 类型 - 也使用 29 位在 AVM+中表示。如果无符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">uint</code>) 的值大于等于 229 或者如果有符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">int</code>) 的值大于等于 228,则它将被 AVM+ 表示为 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型,并使用 AMF 3 double 类型进行序列化。</p>\\n\\n<h4 id=\\"7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</h4>\\n\\n<p>AMF 3 的 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型与 AMF 0 的 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 类型编码方式相同。此类型用于编码 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 或值大于等于 228 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">int</code> 或值大于等于 229 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">uint</code>。编码值始终是网络字节顺序中的 8 字节 IEEE-754 双精度浮点值 (低内存中的符号位)。</p>\\n\\n<h4 id=\\"8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</h4>\\n\\n<p>ActionScript String 值使用 AMF 3 中的单个 string 类型表示 - AMF 0 中的 <code class=\\"language-plaintext highlighter-rouge\\">string</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">long string</code> 类型的概念不再使用。可以使用对隐式字符串引用表中的索引将字符串作为先前发生的字符串的引用发送。字符串使用 UTF-8 编码 - 但是头可以描述字符串文本或字符串引用。空字符串永远不会作为引用发送。</p>\\n\\n<h4 id=\\"9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了新的 XML 类型 (参见 3.13),但是旧版的 XMLDocument 类型在语言中被保留为 <code class=\\"language-plaintext highlighter-rouge\\">flash.xml.XMLDocument</code>。与 AMF 0 类似,<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 的结构需要扁平化为字符串表示以进行序列化。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。XMLDocuments 可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</h4>\\n\\n<p>在 AMF 3 中,ActionScript Date 简单地作为自 1970 年 1 月 1 日午夜 (UTC 时区) 以来的毫秒数进行序列化。不发送本地时区信息。可以使用对隐式对象引用表中的索引将日期作为先前发生的日期实例的引用发送。</p>\\n\\n<h4 id=\\"11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</h4>\\n\\n<p>ActionScript 数组的类型和在数组中的位置是基于它们的索引性质描述的。以下表格概述了这些术语的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">strict</code>:仅包含序数(数字)索引</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">dense</code>:序数索引从 0 开始,并且在连续索引之间不存在间隙(即,从 0 到数组长度的每一个索引都被定义了)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">sparse</code>:包含至少两个索引之间的一个间隙</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">associative</code>:包含至少一个非序数(字符串)索引(有时称为 ECMA 数组)</li>\\n</ul>\\n\\n<p>AMF 将数组分为两部分,密集部分和关联部分。关联部分的二进制表示由名称/值对(可能没有)终止的空字符串。密集部分的二进制表示由密集部分的大小(可能为零)以及有序的值列表(可能没有)组成。在 AMF 中写入的顺序是密集部分的大小,一个以空字符串终止的名称/值对列表,然后是大小的值。数组可以通过使用隐式对象引用表的索引作为先前发生的数组的引用来发送。</p>\\n\\n<h4 id=\\"12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</h4>\\n\\n<p>AMF 3 中有一种类型用于处理 ActionScript 对象和自定义用户类。使用术语 “traits” 来描述类的定义特征。除了 “anonymous” 对象和 “typed” 对象,ActionScript 3.0 还引入了两个进一步的 traits 来描述如何序列化对象,即 “dynamic” 和 “externalizable”。以下表格概述了这些术语和它们的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Anonymous</code>:实际的 ActionScript 对象类型的实例或没有注册别名的类的实例(在反序列化时将其视为对象)。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Typed</code>:具有注册别名的类的实例。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Dynamic</code>:具有动态特征声明的类定义的实例;可以在运行时动态地从实例中添加和删除公共变量成员。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Externalizable</code>:实现 flash.utils.IExternalizable 的类的实例,它完全控制其成员的序列化(特征信息中不包含属性名)。</li>\\n</ul>\\n\\n<p>在这些特征之外,对象的特征信息还可能包括在类上定义的一组公共变量和公共可读写属性名称(即不是函数的公共成员)。成员名称的顺序很重要,因为在特征信息之后的成员值将按照完全相同的顺序出现。这些成员被视为密封成员,因为它们是由类型明确定义的。</p>\\n\\n<p>如果类型是动态的,则在密封成员之后可以包括一个进一步的部分,该部分将动态成员列为名称/值对。当遇到空字符串名称时,继续读取动态成员。</p>\\n\\n<p>对象可以通过使用隐式对象引用表中的索引来作为先前发生对象的引用。此外,还可以通过使用隐式特征引用表中的索引将特征信息发送为先前发生的一组特征的引用。</p>\\n\\n<h4 id=\\"13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了一种新的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型,支持 E4X 语法。为了序列化,需要将 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型展平成字符串表示形式。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。<code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 XML 实例的引用发送。请注意,这种编码对 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 的使用造成了一些理论限制。每个 UTF-8 编码的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例的字节长度最大为 228-1 字节(大约 256 MB)。</p>\\n\\n<h4 id=\\"14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</h4>\\n\\n<p>用于保存字节数组,即 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>。AMF 3 使用可变长度编码 29 位整数序列化此类型,其中包括字节长度前缀,然后是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 的原始字节。<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"15amf3-的使用\\">15、AMF3 的使用</h4>\\n\\n<h5 id=\\"151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</h5>\\n\\n<p>除了序列化 ActionScript 类型外,AMF 还可用于远程服务的异步调用。可使用简单的消息结构将一批请求发送到远程端点。此消息结构的格式为 AMF 0(参见[AMF0])。可以使用特殊的 <code class=\\"language-plaintext highlighter-rouge\\">avmplus-object-marker</code> 类型将上下文头值或消息正文切换到 AMF 3 编码。</p>\\n\\n<h5 id=\\"152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</h5>\\n\\n<p>在 ActionScript 3.0 中,NetConnection 的限定类名是 flash.net.NetConnection。这个类仍然使用响应器来处理远程端点的结果和状态响应,但是现在需要强类型的 Responder 类。完全限定的类名是 flash.net.Responder。除了正常的结果和状态响应之外,NetConnection 还会分发事件,开发人员可以添加监听器。下面是这些事件的概述:</p>\\n\\n<ul>\\n <li>当异常异步抛出时触发,例如来自本机异步代码。</li>\\n <li>当输入或输出错误导致网络操作失败时触发。</li>\\n <li>当 NetConnection 对象报告其状态或错误条件时触发。</li>\\n <li>如果对 NetConnection.call() 的调用尝试连接到调用者安全沙箱外的服务器,则会触发。</li>\\n</ul>\\n\\n<h5 id=\\"153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></h5>\\n\\n<p>ActionScript 3.0 引入了一种新类型,用于支持以字节数组形式处理原始数据,即 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.ByteArray</code>。为了协助 ActionScript 对象序列化和复制,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实现了 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataInput</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataOutput</code>。这些接口指定了帮助将常见类型写入字节流的实用方法。两个感兴趣的方法是 <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput.writeObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">IDataInput.readObject</code>。这些方法使用 AMF 编码对象。使用的 AMF 版本由 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray.objectEncoding</code> 方法控制,该方法可以设置为 AMF 3 或 AMF 0。枚举类型 <code class=\\"language-plaintext highlighter-rouge\\">flash.net.ObjectEncoding</code> 包含 AMF 版本的常量:分别为 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF0</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF3</code>。</p>\\n\\n<p>请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray.writeObject</code> 使用一个版本的 AMF 对整个对象进行编码。与 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 不同,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 不会从 AMF 0 开始,然后将 <code class=\\"language-plaintext highlighter-rouge\\">objectEncoding</code> 属性设置为 AMF 3 并切换到 AMF 3。还请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 为每个 <code class=\\"language-plaintext highlighter-rouge\\">readObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">writeObject</code> 调用使用新的对象、对象特征和字符串的隐式引用表。</p>\\n\\n<h3 id=\\"二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></h3>\\n\\n<h4 id=\\"1amf3-数据格式基础\\">1、AMF3 数据格式基础</h4>\\n\\n<p>首先介绍一下变长整数(Variable Length Integer),比如 UInt32 如下。</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档,这是一种压缩方式的整数存储,且每一字节都对后面的数据具有预知作用。那么字符串如何处理呢?下面是字符串的处理方式,AMF0 和 AMF3 都才用 UTF-8 编码方式,并做如下压缩处理:</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档。</p>\\n\\n<h4 id=\\"2序列化\\">2、序列化</h4>\\n\\n<p>序列化包括 8 位、16 位、32 位,以及 UTF-8 和 UTF-16(I guess)编码的 String,还有原生数据(Raw Data)、变长无符号整数(Variable Length Unsigned Integer)以及 IP 地址。所谓序列化就是按照指定格式编写各种对象、基础数据类型值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryWriter</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Net</span><span class=\\"o\\">::</span><span class=\\"n\\">SocketAddress</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryWriter</span> <span class=\\"n\\">BinaryWriterNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>请注意其中名为 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriterNull</code> 的成员。构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">writeRaw</code> 是简单地封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter::writeRaw()</code>,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入整数实现如下,用的是从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的重载运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span> \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入变长整数,这段代码含义也一目了然,就是读取变长无符号 32 位整数、64 位整数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">21</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span> <span class=\\"c1\\">// 4 bytes maximum</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">22</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">63</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Can give 10 bytes!</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">shift</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入 IP 地址的两个函数暂略。</p>\\n\\n<h4 id=\\"3反序列化\\">3、反序列化</h4>\\n\\n<p>反序列化就是从指定格式的数据中读出各类型的数据值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryReader</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitEncoded</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString8</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString16</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">read16</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read32</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryReader</span> <span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造与析构函数都很简单:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">)</span> <span class=\\"o\\">:</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取原生数据(Raw Data):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写整数,用的是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter</code> 的重载运算符:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读写整数依旧使用从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt8</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt16</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read16</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read32</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取变长整数,分别针对 <code class=\\"language-plaintext highlighter-rouge\\">UInt32</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">UInt64</code>,要理解 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 的变长整数才能理解这个:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt64</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt64</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></h3>\\n\\n<h4 id=\\"1packetreader\\">1、PacketReader</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define PACKETRECV_SIZE 2048\\nclass PacketReader: public BinaryReader {\\npublic:\\n PacketReader(const Poco::UInt8* buffer,Poco::UInt32 size);\\n PacketReader(PacketReader&amp;);\\n virtual ~PacketReader();\\n const Poco::UInt32 fragments;\\n Poco::UInt32 available(); // 可读字节数\\n Poco::UInt8* current();\\n Poco::UInt32 position(); // 获取当前的相对位置(相对于起始位置的)\\n void reset(Poco::UInt32 newPos = 0); // 设定当前位置\\n void shrink(Poco::UInt32 rest);\\n void next(Poco::UInt32 size);\\nprivate:\\n MemoryInputStream _memory;\\n};\\n</code></pre></div></div>\\n\\n<h6 id=\\"11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">current</code>:当前绝对位置(内存地址)</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code>:当前位置(绝对位置)减去缓冲区起始位置</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"12收缩缓冲区\\">1.2、收缩缓冲区</h6>\\n\\n<p>封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">resize</code>。不过由于前面的 <code class=\\"language-plaintext highlighter-rouge\\">if</code> 语句影响,传给 resize 的参数一定不会大于缓冲区的当前大小。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">shrink</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">rest</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">rest</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">available</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"rest %u more upper than available %u bytes\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">rest</span><span class=\\"p\\">,</span><span class=\\"n\\">available</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">rest</span> <span class=\\"o\\">=</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"o\\">+</span> <span class=\\"n\\">rest</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"13构造函数拷贝构造函数和析构函数\\">1.3、构造函数、拷贝构造函数和析构函数</h6>\\n\\n<p>构造函数先调用父类 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReader</code> 的构造函数,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">fragments</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">_memory</code> 输入流的缓冲区。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">fragments</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"c1\\">// Consctruction by copy</span>\\n<span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">fragments</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">fragments</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">PacketReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2packetwriter\\">2、<code class=\\"language-plaintext highlighter-rouge\\">PacketWriter</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PacketWriter</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">BinaryWriter</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">good</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clear</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">limit</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryOutputStream</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pOther</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"21封装memoryoutputstream\\">2.1、封装<code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">good</code>:不过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code> 也是封装的 <code class=\\"language-plaintext highlighter-rouge\\">std::ostream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">good</code> 函数,True if no error flags are set.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">good</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">good</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">written</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">length</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code>:设置缓冲区的指针位置,即 <code class=\\"language-plaintext highlighter-rouge\\">position</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code>:移动缓冲区指针</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code>:返回缓冲区的起始地址</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">clear</code>:其实就是修改 written 和 position,使得指定位置后面的数据在以后写的时候可以被覆盖,并不是真正的清除。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">clear</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">limit</code>:封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">limit</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">length</span> <span class=\\"o\\">==</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">length</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">length</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Limit '%d' more upper than buffer size '%d' bytes\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">length</span><span class=\\"p\\">,</span><span class=\\"n\\">_size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">length</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">length</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"22封装-binarywriter\\">2.2、封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">flush</code>:封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">flush</code>,不过 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">flush</code> 实际上是从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter</code> 继承而来的。其作用是「Flushes the underlying stream」。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">flush</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_pOther</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_pOther</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">_pOther</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"23构造函数拷贝构造函数和析构函数\\">2.3、构造函数、拷贝构造函数和析构函数</h6>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pOther</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_size</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"c1\\">// Consctruction by copy</span>\\n<span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_pOther</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_size</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>注意析构函数中会进行 <code class=\\"language-plaintext highlighter-rouge\\">flush</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::~</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"四amfreader\\">四、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code></h3>\\n\\n<h4 id=\\"1objectdef\\">1、<code class=\\"language-plaintext highlighter-rouge\\">ObjectDef</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ObjectDef</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span> \\n <span class=\\"n\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">(</span><span class=\\"n\\">amf3</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">dynamic</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">externalizable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">count</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">arrayType</span><span class=\\"p\\">(</span><span class=\\"n\\">arrayType</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">string</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">hardProperties</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reset</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">dynamic</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">externalizable</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">count</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2amfreader-定义\\">2、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 定义</h4>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 作为其成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">AMFReader</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">AMFReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readSimpleObject</span><span class=\\"p\\">(</span><span class=\\"n\\">AMFSimpleObject</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">object</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">read</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">readNumber</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">readInteger</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readBoolean</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">readByteArray</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Timestamp</span> <span class=\\"n\\">readDate</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readObject</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readArray</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readDictionary</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">weakKeys</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readKey</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readItem</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">readRawObjectContent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readNull</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">startReferencing</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">stopReferencing</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">;</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*&gt;</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_stringReferences</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_classDefReferences</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_amf0References</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_amf0Reset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_reset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_amf3</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">_referencing</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数析构函数\\">2.1、构造函数、析构函数</h5>\\n\\n<p>参数为 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code>,会初始化一些成员变量。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">reader</span><span class=\\"p\\">(</span><span class=\\"n\\">reader</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_amf3</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_amf0Reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_referencing</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构时,会逐一释放 <code class=\\"language-plaintext highlighter-rouge\\">_objectDefs</code> 中对象的内存:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMFReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*&gt;::</span><span class=\\"n\\">iterator</span> <span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">delete</span> <span class=\\"o\\">*</span><span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22简单封装-packetreader-的一些函数\\">2.2、简单封装 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code>:操作指针位置</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_reset</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code>:根据当前缓冲区大小和 <code class=\\"language-plaintext highlighter-rouge\\">written</code> 计算得到</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">current</code>:<code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 内存地址</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">*</span><span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23设置-gptr-位置\\">2.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 位置</h5>\\n\\n<p>其实 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 也被影响了,但是在 <code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 中只用 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>。调用构造函数的时候,<code class=\\"language-plaintext highlighter-rouge\\">reset</code> 被设为 0,其后在每次读取数据的时候都会影响 <code class=\\"language-plaintext highlighter-rouge\\">reset</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_reset</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24判断类型\\">2.4、判断类型</h5>\\n\\n<p>分析请看注释:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">followingType</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先 <code class=\\"language-plaintext highlighter-rouge\\">reset</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">back</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">amf3</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>是 AMF0 类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果没有可读数据了,则返回 AMF::End。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">available</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">End</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>开始读了,先读到的表示 AMF 数据类型。要注意的是调用 current 并不改变指针的位置,所以你会在线面看到调用 next。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt8</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_amf3</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF_AVMPLUS_OBJECT</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">available</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">End</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>AMF3 类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>Undefined 和 null 都当做 null。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_UNDEFINED</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_NULL</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>false 和 true 都是 boolean。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_FALSE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_TRUE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_INTEGER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Integer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_NUMBER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">String</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_DATE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Array</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_DICTIONARY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Dictionary</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Object</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_BYTEARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">ByteArray</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>落到 default 手里的话,就跳过这个字节,读取下一个。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unknown AMF3 type %.2x\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>AMF0 类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">switch</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">null</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">null</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_UNDEFINED</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_NULL</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BOOLEAN</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_NUMBER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">long string</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">string</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">string</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_LONG_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">String</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">mixed array</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">strict array</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">array</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_MIXED_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_STRICT_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Array</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_DATE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin object</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">begin typed object</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">object</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BEGIN_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BEGIN_TYPED_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Object</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是引用,就跳过表示类型值的这个字节。这个先留下来,带我们分析完 readArray 和 readObject 再回头看。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_REFERENCE</span><span class=\\"p\\">:</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read16</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">reference</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_amf0References</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF0 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_amf0Reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_amf0References</span><span class=\\"p\\">[</span><span class=\\"n\\">reference</span><span class=\\"p\\">]);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果没了,或者不支持,或者都不是,就跳过这个字节,递归继续读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_END_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF end object type without begin object type before\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_UNSUPPORTED</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unsupported type in AMF format\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">default</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unknown AMF type %.2x\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">followingType</code> 是这个类的核心,每个具体的数据类型的分析都依赖于它的判断。这些类型的解析,会在下一篇文章中介绍。</p>\\n\\n<h4 id=\\"3解析-as3-null\\">3、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Null</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readNull</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先 reset 一下是惯例,就像糗百上的割一样。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span> \\n</code></pre></div></div>\\n\\n<p>AMF 数据类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">Null</code>,跳过该字节,并返回</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>判断错误</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Null type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4解析-as3-number\\">4、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Number</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">double</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readNumber</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Null</code> 会被悲催的跳过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 呀 :(</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Number type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过该字节吧</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>返回吧,返回之前还用到 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 的运算符,注意 AS3 中的 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 就是 C++ 的 <code class=\\"language-plaintext highlighter-rouge\\">double</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5解析-as3-integer\\">5、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Int32</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readInteger</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code> 类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>Null 的话:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code> 或者 Number 的话。。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Integer</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Integer type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过吧。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>终于是 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读一个变长的 32 位无符号整数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Forced in AMF3 here!</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果大于 3.5 个字节所能表示的最大无符号整数值(<code class=\\"language-plaintext highlighter-rouge\\">268435455</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">0xFFFFFFF</code>),则减去 <code class=\\"language-plaintext highlighter-rouge\\">0x2FFFFFFF</code>(这还不是太理解,有能解释的朋友给留个言,或者发 email 给我 )</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">268435455</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">value</span> <span class=\\"o\\">-=</span> <span class=\\"p\\">(</span><span class=\\"mi\\">1</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"mi\\">29</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"6解析-as3-boolean\\">6、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readBoolean</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">Null</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>居然不是 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code> 的话。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Boolean type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 的话,返回 <code class=\\"language-plaintext highlighter-rouge\\">true</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">false</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span><span class=\\"o\\">==</span> <span class=\\"n\\">AMF3_FALSE</span> <span class=\\"o\\">?</span> <span class=\\"nb\\">false</span> <span class=\\"o\\">:</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">AMF0</code> 喽:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span><span class=\\"o\\">==</span><span class=\\"mh\\">0x00</span> <span class=\\"o\\">?</span> <span class=\\"nb\\">false</span> <span class=\\"o\\">:</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"7开始引用与结束引用\\">7、开始引用与结束引用</h4>\\n\\n<p>如下这两个函数会在 <code class=\\"language-plaintext highlighter-rouge\\">FlowConnection</code> 中调用。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">startReferencing</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_referencing</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">stopReferencing</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_referencing</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"8解析-as3-bytearray\\">8、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code></h4>\\n\\n<p>先回顾一下 AMF3 中的ByteArray 的数据格式:</p>\\n\\n<p>注意到,首先要读取一个变长无符号 32 位整数,但是最低位是 1,只有 28 位用于表示数据长度。解释完这里,下面的解析过程才好理解。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readByteArray</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Null</code> 就返回 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReaderNull</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>,也返回 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReaderNull</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">ByteArray</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF ByteArray type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过这个字节:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>注意 position 返回的是相对位置,与 AS3 中一样。<code class=\\"language-plaintext highlighter-rouge\\">reference</code> 表示这个地址(简单说,引用就是地址嘛)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>读取一个变长 32 位无符号整数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>最低位是 1 的话,<code class=\\"language-plaintext highlighter-rouge\\">isInline</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,否则为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>右移一位,因为那一位是标志位,上面解释过了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">isInline</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,表示是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">_referencing</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code> 的话(这是一个 <code class=\\"language-plaintext highlighter-rouge\\">vector</code>),push back this reference:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不符合 ByteArray 的格式定义的话:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">size</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>移动到这个 reference 的位置,<code class=\\"language-plaintext highlighter-rouge\\">_references[size]</code> 就是这个位置(相对)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">size</span><span class=\\"p\\">]);</span> <span class=\\"c1\\">// TODO size 作为索引,还没有完全理解</span>\\n</code></pre></div></div>\\n\\n<p>读取这个 reference 的 size 值给 size对象(注意 size 是这个函数传入的引用参数,其值可以被修改)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>把读取完 ByteArraty 的 PacketReader 返回:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>最后强调一点,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 的数据段最大长度为 228 -1 字节,约为 256 MB。</p>\\n\\n<h4 id=\\"9解析-as3-date\\">9、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Date</code></h4>\\n\\n<p>先看下 <code class=\\"language-plaintext highlighter-rouge\\">Date</code> 的数据格式:</p>\\n\\n<p>下面开始分析:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Timestamp</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readDate</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>Null 的话,就返回当前时间:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是 Date 类型,也返回当前时间:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Date type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是 AMF3:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先读取 flag,最低一位必须是 1,其他位丢到垃圾桶。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">flags</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>当前相对位置。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>是 1 就 push back 到 <code class=\\"language-plaintext highlighter-rouge\\">_references</code> 里。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">flags</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>读取一个 double,到 result 里(result 也是 double 类型哦~)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位不是 1,麻烦不少哒。。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flags</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果 flag 超了,就返回当前时间作为时间戳作为 Date。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">flags</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这段与 ByteArray 那段一样:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">flags</span><span class=\\"p\\">]);</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>返回喽~</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"nf\\">Timestamp</span><span class=\\"p\\">((</span><span class=\\"n\\">Timestamp</span><span class=\\"o\\">::</span><span class=\\"n\\">TimeVal</span><span class=\\"p\\">)</span> <span class=\\"n\\">result</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>读俩,因为是 double(64 位):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">2</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Timezone, useless</span>\\n</code></pre></div></div>\\n\\n<p>返回喽~</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"nf\\">Timestamp</span><span class=\\"p\\">((</span><span class=\\"n\\">Timestamp</span><span class=\\"o\\">::</span><span class=\\"n\\">TimeVal</span><span class=\\"p\\">)</span> <span class=\\"n\\">result</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"10解析-as3-dictionary\\">10、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Dictionary</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readDictionary</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">weakKeys</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>下面这段咱就略了。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Dictionary</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Dictionary type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过 type:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// AMF3</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// marker</span>\\n</code></pre></div></div>\\n\\n<p>当前相对位置值作为 <code class=\\"language-plaintext highlighter-rouge\\">reference</code>,再读个 <code class=\\"language-plaintext highlighter-rouge\\">size</code>,还是最低位必须为 1,不是就返回 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">size</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">isInline</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">size</span><span class=\\"o\\">&gt;</span><span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>下面要调用到 <code class=\\"language-plaintext highlighter-rouge\\">ObjectRef</code> 构造函数,这里再把其实现拿出来看看,其实主要是初始化了哪些成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">(</span><span class=\\"n\\">amf3</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">dynamic</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">externalizable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">count</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">arrayType</span><span class=\\"p\\">(</span><span class=\\"n\\">arrayType</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>可以看到要有一个 amf3,还有 <code class=\\"language-plaintext highlighter-rouge\\">reset</code> 置为 0,<code class=\\"language-plaintext highlighter-rouge\\">dynamic</code> 置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,<code class=\\"language-plaintext highlighter-rouge\\">externalizable</code> 也是 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,<code class=\\"language-plaintext highlighter-rouge\\">count</code> 是 0,<code class=\\"language-plaintext highlighter-rouge\\">arrayType</code> 成员要赋值。</p>\\n\\n<p>上面是插播哦,下面还要继续哒。创建这么一个对象,注意是 new 出来的,所以我们在《OpenRTMFP/Cumulus Primer(16)AMF解析之AMFReader》一文中提到了 AMFReader 的析构函数中要对 <code class=\\"language-plaintext highlighter-rouge\\">_objectRef</code> 的每个元素逐一析构的。<code class=\\"language-plaintext highlighter-rouge\\">arrayType</code> 就设置为 <code class=\\"language-plaintext highlighter-rouge\\">AMF3_DICTIONARY</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*</span> <span class=\\"n\\">pObjectDef</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nf\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">,</span> <span class=\\"n\\">AMF3_DICTIONARY</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">dynamic</span><span class=\\"o\\">=</span><span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pObjectDef</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位是 1,就直接 push back,跟之前一样。不过这里多了一个 <code class=\\"language-plaintext highlighter-rouge\\">pObjectDef</code>,所以还要设置一下它的计数为 <code class=\\"language-plaintext highlighter-rouge\\">size</code>,就是 <code class=\\"language-plaintext highlighter-rouge\\">dictionary</code> 数据大小。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位是 0,就把 <code class=\\"language-plaintext highlighter-rouge\\">count</code> 设置为下一个变长整数值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">size</span><span class=\\"p\\">]);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">*=</span> <span class=\\"mi\\">2</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>读一个字节,如果最小位是 1,weakKeys 就是 true,否则为 false。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">weakKeys</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">return</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</title>\\n \\t<meta name=\\"description\\" content=\\"CumulusServer 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 CumulusEdge 和 CumulusServer 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 CumulusServer 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-15T14:26:58+00:00\\" class=\\"by-line\\">15 Apr 2012, 广州 | 作者 麦克船长 | 总计 3844 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1绑定地址\\" id=\\"markdown-toc-1绑定地址\\">1、绑定地址</a></li>\\n <li><a href=\\"#2cumulusserver-接收数据\\" id=\\"markdown-toc-2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</a></li>\\n <li><a href=\\"#3如果-cumulusedge-端口存在且-edge-socket-可用\\" id=\\"markdown-toc-3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</a></li>\\n <li><a href=\\"#4cumulusserver-和-cumulusedge-的-socket-都没有数据\\" id=\\"markdown-toc-4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</a></li>\\n <li><a href=\\"#5发送方的-ip-被禁\\" id=\\"markdown-toc-5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</a></li>\\n <li><a href=\\"#6数据包长度小于可能的最小值12\\" id=\\"markdown-toc-6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</a></li>\\n</ul>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。</p>\\n\\n<p>本所要介绍的这个主循环在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(const volatile bool&amp; terminate)</code> 函数中。RTMFPServer覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"1绑定地址\\">1、绑定地址</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">address</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">绑定</span><span class=\\"n\\">CumulusEdge</span><span class=\\"err\\">的</span> <span class=\\"n\\">IP</span> <span class=\\"err\\">地址和端口:</span>\\n\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">edgesAddress</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>发送者(Client)的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"n\\">sender</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">buff</span><span class=\\"p\\">[</span><span class=\\"n\\">PACKETRECV_SIZE</span><span class=\\"p\\">];</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">stop</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">idle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">realTime</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 读取:把数据存到 <code class=\\"language-plaintext highlighter-rouge\\">buff</code>,把发送者地址赋给 <code class=\\"language-plaintext highlighter-rouge\\">sender</code>,把所读长度返回给 <code class=\\"language-plaintext highlighter-rouge\\">size</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>处理 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span> <span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span> <span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span> <span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">Edge</span><span class=\\"o\\">*</span> <span class=\\"n\\">pEdge</span> <span class=\\"o\\">=</span> <span class=\\"n\\">edges</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pEdge</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pEdge</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 空闲:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">idle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>主线程等待一秒。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">isElapsed</span><span class=\\"p\\">(</span><span class=\\"n\\">_freqManage</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>Just middle session</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Sessions</span><span class=\\"o\\">::</span><span class=\\"n\\">Iterator</span> <span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Middle</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMiddle</span> <span class=\\"o\\">=</span> <span class=\\"k\\">dynamic_cast</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Middle</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMiddle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pMiddle</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isBanned</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">INFO</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Data rejected because client %s is banned\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">().</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">size</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">RTMFP_MIN_PACKET_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Invalid packet\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">PacketReader</span> <span class=\\"nf\\">packet</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Session</span><span class=\\"o\\">*</span> <span class=\\"n\\">pSession</span> <span class=\\"o\\">=</span> <span class=\\"n\\">findSession</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFP</span><span class=\\"o\\">::</span><span class=\\"n\\">Unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">));</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">checked</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">commitCookie</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pSession</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>给 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 或者自己(<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>)的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">setEndPoint</span><span class=\\"p\\">(</span><span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">?</span> <span class=\\"n\\">_edgesSocket</span> <span class=\\"o\\">:</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">,</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">delete</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的第二篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-14T11:20:46+00:00\\" class=\\"by-line\\">14 Apr 2012, 广州 | 作者 麦克船长 | 总计 18890 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一cumulus-启动源码分析\\" id=\\"markdown-toc-一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数\\" id=\\"markdown-toc-1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</a></li>\\n <li><a href=\\"#2maincpp-中的-cumulusserver-构造函数\\" id=\\"markdown-toc-2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</a></li>\\n <li><a href=\\"#3maincpp-中的-cumulusserver-的-initialize-成员函数\\" id=\\"markdown-toc-3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</a></li>\\n <li><a href=\\"#4maincpp-中的-cumulusserver-的-main-成员函数\\" id=\\"markdown-toc-4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</a></li>\\n <li><a href=\\"#5cumulusserver-是-serverapplication-的子类\\" id=\\"markdown-toc-5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</a></li>\\n <li><a href=\\"#6serverapplication-是-application-的子类\\" id=\\"markdown-toc-6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</a></li>\\n <li><a href=\\"#7反初始化\\" id=\\"markdown-toc-7反初始化\\">7、反初始化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二cumulusserver-各项交互功能的源码解读\\" id=\\"markdown-toc-二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</a> <ul>\\n <li><a href=\\"#1命令行选项设定\\" id=\\"markdown-toc-1命令行选项设定\\">1、命令行选项设定</a></li>\\n <li><a href=\\"#2处理命令行选项\\" id=\\"markdown-toc-2处理命令行选项\\">2、处理命令行选项</a></li>\\n <li><a href=\\"#6dump-logs\\" id=\\"markdown-toc-6dump-logs\\">6、Dump logs</a></li>\\n <li><a href=\\"#3停止运行\\" id=\\"markdown-toc-3停止运行\\">3、停止运行</a></li>\\n <li><a href=\\"#4载入配置\\" id=\\"markdown-toc-4载入配置\\">4、载入配置</a></li>\\n <li><a href=\\"#5处理日志\\" id=\\"markdown-toc-5处理日志\\">5、处理日志</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三maincpp-的-main-函数源码分析\\" id=\\"markdown-toc-三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数中的-server\\" id=\\"markdown-toc-1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></a></li>\\n <li><a href=\\"#2maincpp-中-main-函数的-serverstart\\" id=\\"markdown-toc-2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></a></li>\\n <li><a href=\\"#3回顾一下整个启动流程\\" id=\\"markdown-toc-3回顾一下整个启动流程\\">3、回顾一下整个启动流程</a></li>\\n <li><a href=\\"#4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\" id=\\"markdown-toc-4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。首先要知道的是,OpenRTMFP/Cumulus 中使用到的库有 Poco、OpenSSL 和 Lua。</p>\\n\\n<h3 id=\\"一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</h4>\\n\\n<p>入口在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"kt\\">int</span> <span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">argv</span><span class=\\"p\\">[])</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先检查内存泄露,不过目前这个开发中的项目还没有实现这个功能,只是个空函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">DetectMemoryLeak</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后会创建一个匿名的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象,并调用其 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,该函数由 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 从 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 中继承而来,而 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 由是从 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 继承而来的。<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象调用 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,实际是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是调用 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的函数,而该 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。如果 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数被调用时抛出异常,则不会执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,但仍然会执行 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Runs the application by performing additional initializations</span>\\n <span class=\\"c1\\">// and calling the main() method.</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">CumulusServer</span><span class=\\"p\\">().</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"n\\">argv</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">()</span><span class=\\"o\\">:</span> <span class=\\"n\\">_helpRequested</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span> <span class=\\"c1\\">// 显示帮助信息</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_middle</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_isInteractive</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pLogFile</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</h4>\\n\\n<p>在执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数之前,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 会启动 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,传入的参数就是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 自己,可以猜到 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run</code> 方法中,调用该函数时的参数是 <code class=\\"language-plaintext highlighter-rouge\\">this</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">Application</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">self</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>调用父函数 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的初始化函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">self</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>再继续下面的源码分析之前,先要知道,根据 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> ,<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 类有一些内置的配置属性,如下:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.path</code>: 可执行文件的绝对路径;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.name</code>: 可执行文件的文件名(含扩展名);</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.baseName</code>: 可执行文件的文件名(不含扩展名)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.dir</code>: 可执行文件的所在目录;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.configDir</code>: 配置文件所在目录;</li>\\n</ul>\\n\\n<p>所以下面就读取了可执行文件的所在目录,其中第二个参数表示默认值(即当前目录):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>然后读取配置文件,目录为上一句所得到的 <code class=\\"language-plaintext highlighter-rouge\\">dir</code>,文件名(不含扩展名)为 <code class=\\"language-plaintext highlighter-rouge\\">application.basename</code> 内置配置属性值,其默认值为 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>,然后加上点和扩展名 <code class=\\"language-plaintext highlighter-rouge\\">.ini</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">dir</span>\\n <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.baseName\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"CumulusServer\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">+</span> <span class=\\"s\\">\\".ini\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这样就加载完配置了。然后查看当前的进程是从命令行运行的(命令行是交互的,所以是 interactive),还是以 daemon 方式运行的,这个函数是ServerApplication的一个成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_isInteractive</span> <span class=\\"o\\">=</span> <span class=\\"n\\">isInteractive</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后获取表示日志文件所在目录的字符串,其中 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code> 是外置配置属性(配置文件中),其默认值为上面获取到的可执行文件路径(一般为当前路径)与 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 的组合,即一般为当前目录下的 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"nf\\">logDir</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.directory\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"logs\\"</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>创建日志文件目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">logDir</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n\\n</code></pre></div></div>\\n\\n<p>日志文件绝对路径,<code class=\\"language-plaintext highlighter-rouge\\">logs</code> 为默认的日志文件名(不含扩展名的部分),扩展名用0:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logDir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span> <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.name\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\".\\"</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pLogFile</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nf\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"0\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>用日志流打开日志文件(方式为追加写入):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Logs</code> 是一个方法类(其中的 <code class=\\"language-plaintext highlighter-rouge\\">public</code> 函数都是静态的),<code class=\\"language-plaintext highlighter-rouge\\">SetLogger</code> 的作用就是将Logs中的似有静态成员设置为某个 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code> 对象(由于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLogger</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</h4>\\n\\n<p>OpenRTMFP/Cumulus 是一个基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的服务端应用(准确的说是基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::ServerApplication</code> 的服务端应用)。如果没有特殊的启动要求,可以调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco/Application.h</code> 中定义的宏 <code class=\\"language-plaintext highlighter-rouge\\">POCO_APP_MAIN</code> 来完成初始化、日志和启动(该宏会根据不同的平台启用不同的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数)。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数在调用完 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数后,会调用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,该 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的定义在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&gt;&amp;</span> <span class=\\"n\\">args</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>首先看是否是要求帮助信息,<code class=\\"language-plaintext highlighter-rouge\\">displayHelp</code> 是借助 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::HelpFormatter</code> 实现的,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数会在调用时将 <code class=\\"language-plaintext highlighter-rouge\\">_helpRequested</code> 设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_helpRequested</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">displayHelp</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是,则进入启动状态,首先创建一个 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServerParams</code> 对象 <code class=\\"language-plaintext highlighter-rouge\\">params</code>,用来存储 OpenRTMFP/CumulusServer 的基本配置信息。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">RTMFPServerParams</span> <span class=\\"n\\">params</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">params</code> 存储 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的端口号和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的端口号:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"port\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RTMFP_DEFAULT_PORT</span><span class=\\"o\\">+</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getBool</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.activated\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port must have a positive value if \\\\\\n edges.activated is true. Server edges is \\\\\\n disactivated.\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">=</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">_pCirrus</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">SocketAddress</code> 的成员,是封装IP地址和端口号的对象。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_middle</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>UDB 所使用的缓冲区大小:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"udpBufferSize\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"keepAliveServer\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"keepAlivePeer\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>失败前 CumulusEdge 的尝试次数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"edges.attemptsBeforeFallback\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> 函数是 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中必须调用的,意为等待终止运行的操作请求。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// wait for CTRL-C or kill</span>\\n <span class=\\"n\\">waitForTerminationRequest</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>一旦接收到终止操作的请求,就会执行下面这句,用以退出 OpenRTMFP/Cumulus 的运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Stop the server</span>\\n <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">stop</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">catch</code> 一些可能产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Configuration problem : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>OpenRTMFP/CumulusServer 停止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"n\\">Application</span><span class=\\"o\\">::</span><span class=\\"n\\">EXIT_OK</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 对其子类有如下要求:</p>\\n\\n<ul>\\n <li>Subsystems must be registered in the constructor.</li>\\n <li>All non-trivial initializations must be made in the <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>` method.</li>\\n <li>At the end of the <code class=\\"language-plaintext highlighter-rouge\\">main()</code> method, <code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> should be called.</li>\\n</ul>\\n\\n<h4 id=\\"6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</h4>\\n\\n<p>Application 对其子类的要求是,如下这些成员函数必须被覆盖:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> (the one-argument, protected variant):上一篇已介绍过。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code>:下面会介绍,Application 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会在调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">reinitialize()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">defineOptions()</code>:定义命令行启动选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">handleOption()</code>:响应相应的命令行选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">main()</code></li>\\n</ul>\\n\\n<h4 id=\\"7反初始化\\">7、反初始化</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的。<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code>,最后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code>。最后这个反初始化过程,在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 就是直接调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">uninitialize</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">uninitialize</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</h3>\\n\\n<h4 id=\\"1命令行选项设定\\">1、命令行选项设定</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的命令行选项有:<code class=\\"language-plaintext highlighter-rouge\\">log(l)</code>、<code class=\\"language-plaintext highlighter-rouge\\">dump(d)</code>、<code class=\\"language-plaintext highlighter-rouge\\">cirrus(c)</code>、<code class=\\"language-plaintext highlighter-rouge\\">middle(m)</code>、<code class=\\"language-plaintext highlighter-rouge\\">help(h)</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">OptionSet</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">options</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>设定日志级别(0 - 8,默认是 6,表示 info 级别)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"l\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Log level argument, must be beetween 0 and 8 : \\\\\\n nothing, fatal, critic, error, warn, note, info, debug, trace. \\\\\\n Default value is 6 (info), all logs until info level are displayed.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">argument</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"level\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>其他一些选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"d\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Enables packet traces in logs. Optional arguments \\\\\\n are 'middle' or 'all' respectively to displays just middle packet \\\\\\n process or all packet process. If no argument is given, just outside \\\\\\n packet process will be dumped.\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"middle|all\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"c\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Cirrus address to activate a 'man-in-the-middle' \\\\\\n developer mode in bypassing flash packets to the official cirrus \\\\\\n server of your choice, it's a instable mode to help Cumulus developers, \\\\\\n </span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\">p2p.rtmfp.net:10000</span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\"> for example. By adding the 'dump' argument, \\\\\\n you will able to display Cirrus/Flash packet exchange in your logs \\\\\\n (see 'dump' argument).\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"address\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"m\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"Enables a 'man-in-the-middle' developer mode \\\\\\n between two peers. It's a instable mode to help Cumulus developers. \\\\\\n By adding the 'dump' argument, you will able to display Flash/Flash \\\\\\n packet exchange in your logs (see 'dump' argument).\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>显示帮助信息的选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"h\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Displays help information about command-line usage.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">OptionSet</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::OptionSet</code>,调用addOption可以向其中增加选项Option。其中required和repeatable表示:</p>\\n\\n<p>Sets whether the option is required (flag == true) or optional (flag == false).\\nReturns true if the option can be specified more than once, or false if at most once.\\n当需要显示帮助信息时,调用如下函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">displayHelp</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">HelpFormatter</span> <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setCommand</span><span class=\\"p\\">(</span><span class=\\"n\\">commandName</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setUsage</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"OPTIONS\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setHeader</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer, open source RTMFP server\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">cout</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setCommand()</code>: Sets the command name.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setUsage()</code>: Sets the usage string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setHeader()</code>: Sets the header string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">format()</code>: Writes the formatted help text to the given stream.</li>\\n</ul>\\n\\n<h4 id=\\"2处理命令行选项\\">2、处理命令行选项</h4>\\n\\n<p>参数是选项名和选项值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是帮助:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_helpRequested</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">URI</span> <span class=\\"n\\">uri</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"rtmfp://\\"</span><span class=\\"o\\">+</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">SocketAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getHost</span><span class=\\"p\\">(),</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getPort</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' : the exchange will bypass to '%s'\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' error : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">message</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是dump日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"all\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">ALL</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">MIDDLE</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">EXTERNAL</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是log,表示设定日志级别:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLevel</span><span class=\\"p\\">(</span><span class=\\"n\\">atoi</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">()));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"6dump-logs\\">6、Dump logs</h4>\\n\\n<p>先加一个作用域锁,然后再向日志流写数据。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">dumpHandler</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">cout</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>调用 manageLogFile,主要做一些日志大小超出限制的处理。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">manageLogFile</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先判断是否超过日志文件的大小上线,LOG_SIZE是1000000字节(即约 1 MB)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">getSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">LOG_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">num</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>打开新日志文件:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span> <span class=\\"nf\\">file</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"10\\"</span><span class=\\"p\\">);</span>\\n\\n</code></pre></div></div>\\n\\n<p>如果该文件已经存在,则先删除:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">remove</span><span class=\\"p\\">();</span>\\n\\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">--</span><span class=\\"n\\">num</span> <span class=\\"o\\">&gt;=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">renameTo</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span> <span class=\\"o\\">+</span> <span class=\\"mi\\">1</span><span class=\\"p\\">));</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span> \\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3停止运行\\">3、停止运行</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code>,该类中有纯虚函数 <code class=\\"language-plaintext highlighter-rouge\\">kill()</code> 需要被实现,于是有:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">kill</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">terminate</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code> 的定义在 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller.h</code> 中,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ApplicationKiller</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n \\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">kill</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4载入配置\\">4、载入配置</h4>\\n\\n<p>在initialize()函数中调用,上一篇已提到过。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">path</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5处理日志\\">5、处理日志</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">logHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">TID</span> <span class=\\"n\\">threadId</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">threadName</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Priority</span> <span class=\\"n\\">priority</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">filePath</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">long</span> <span class=\\"n\\">line</span><span class=\\"p\\">,</span> \\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">text</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>作用域锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">Path</span> <span class=\\"nf\\">path</span><span class=\\"p\\">(</span><span class=\\"n\\">filePath</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">string</span> <span class=\\"n\\">file</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getExtension</span><span class=\\"p\\">()</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"lua\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">directory</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">depth</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是命令行交互模式(即不是 daemon 模式):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_isInteractive</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">printf</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"%s %s[%ld] %s</span><span class=\\"se\\">\\\\n</span><span class=\\"s\\">\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">],</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getBaseName</span><span class=\\"p\\">()).</span><span class=\\"n\\">c_str</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">line</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">text</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>向日志流输出一句日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">DateTimeFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">LocalDateTime</span><span class=\\"p\\">(),</span><span class=\\"s\\">\\"%d/%m %H:%M:%S.%c \\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">]</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'\\\\t'</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadName</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'('</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadId</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\")</span><span class=\\"se\\">\\\\t</span><span class=\\"s\\">\\"</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getFileName</span><span class=\\"p\\">())</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'['</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">line</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\"] \\"</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">text</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">endl</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>日志文件的善后处理(主要处理文件大小限制可能产生的问题):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中真正启动的是 <code class=\\"language-plaintext highlighter-rouge\\">server</code>,它继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code>,而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code> 又继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Gateway</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Handler</code>。而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code> 继承自 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Runnable</code>,所以其是一个可以运行的线程。在 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 中,这是主线程。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span> <span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n<span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/Server.h</code> 中定义的,其构造函数的原型为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>个参数含义如下:</p>\\n\\n<blockquote>\\n <p>The Path Root for the Server Application Killer for Termanting the Server Application Server Configuration</p>\\n</blockquote>\\n\\n<p>距离来说,在我的 Worksapce 中:</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">root</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 构造函数的初始化列表极长:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"o\\">::</span><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">)</span> \\n <span class=\\"o\\">:</span> <span class=\\"n\\">_blacklist</span><span class=\\"p\\">(</span><span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"blacklist\\"</span><span class=\\"p\\">,</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_applicationKiller</span><span class=\\"p\\">(</span><span class=\\"n\\">applicationKiller</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_hasOnRealTime</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pService</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">luaMail</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"o\\">=</span><span class=\\"n\\">Script</span><span class=\\"o\\">::</span><span class=\\"n\\">CreateState</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.host\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"localhost\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">SMTPSession</span><span class=\\"o\\">::</span><span class=\\"n\\">SMTP_PORT</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.timeout\\"</span><span class=\\"p\\">,</span><span class=\\"mi\\">60</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>下面调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::File</code> 创建目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">((</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">WWWPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"www\\"</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>因为 <code class=\\"language-plaintext highlighter-rouge\\">roor</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 目录,所以 <code class=\\"language-plaintext highlighter-rouge\\">WWWPath</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/www</code> 目录。然后初始化 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code>,这个 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code> 是和 Lua 有关的东东,这里暂不细说,先知道与 Lua 相关就好。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Service</span><span class=\\"o\\">::</span><span class=\\"n\\">InitGlobalTable</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>下面就涉及到了 Lua script 了:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SCRIPT_BEGIN</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">SCRIPT_CREATE_PERSISTENT_OBJECT</span><span class=\\"p\\">(</span><span class=\\"n\\">Invoker</span><span class=\\"p\\">,</span><span class=\\"n\\">LUAInvoker</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">readNextConfig</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"n\\">configurations</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">lua_setglobal</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"cumulus.configs\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">SCRIPT_END</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN</code>、<code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_CREATE_PERSISTENT_OBJECT和SCRIPT_END</code> 都是宏,其定义在 <code class=\\"language-plaintext highlighter-rouge\\">Script.h</code> 文件中,如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define SCRIPT_BEGIN(STATE) \\\\\\n if (lua_State* __pState = STATE) { \\\\\\n const char* __error=NULL;\\n \\n#define SCRIPT_CREATE_PERSISTENT_OBJECT(TYPE,LUATYPE,OBJ) \\\\\\n Script::WritePersistentObject&lt;TYPE,LUATYPE&gt;(__pState,OBJ); \\\\\\n lua_pop(__pState,1);\\n \\n#define SCRIPT_END }\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN和SCRIPT_END</code> 经常用到,当与 Lua 相关的操作出现时,都会以这两个宏作为开头和结尾。</p>\\n\\n<h4 id=\\"2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServerParams</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">params</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 正在运行,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer server is yet running, call stop method before\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定端口号,如果端口号为 0,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must have a positive value\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusEdge</code> 的端口号,如果其端口号与 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusSever</code> 端口号相同,则返回并终止启动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"n\\">_edgesPort</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must different than RTMFPServer edges.port\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>Cirrus:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">2000000</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// 2 sec by default</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">Target</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// no waiting, direct process in the middle case!</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode with server %s \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pCirrus</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">address</span><span class=\\"p\\">.</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode between peers \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>UDP Buffer:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span> <span class=\\"o\\">?</span> \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">getReceiveBufferSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Socket buffer receving/sending size = %u/%u\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">threadPriority</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>启动线程,进入循环运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>上句具体的源码实现为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果在运行则返回并终止启动。然后加一个局部锁。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果不得不join()到主线程中,那就join()吧</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后就运行这个线程吧:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3回顾一下整个启动流程\\">3、回顾一下整个启动流程</h4>\\n\\n<p>到此我们先回顾一下启动过程:</p>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的启动入口 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数开始,创建 <code class=\\"language-plaintext highlighter-rouge\\">Server</code> 对象并启动(调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 函数)。<code class=\\"language-plaintext highlighter-rouge\\">Server::start()</code> 中调用其父类(<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code>)的父类(<code class=\\"language-plaintext highlighter-rouge\\">Startable</code>)的方法 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 开启线程。\\n调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 函数后,开启线城时传入的参数为 <code class=\\"language-plaintext highlighter-rouge\\">*this</code>,所以就会运行 <code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code>;</p>\\n\\n<h4 id=\\"4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code> 调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,但这个函数被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖,所以会运行 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>,其源码如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果CumulusEdge:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP edges server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">onStart</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>运行线程:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>处理异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果跳出了,则终止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">onStop</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server stops\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>该函数内部又会调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,该函数调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>它是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 实现。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 会调用 <code class=\\"language-plaintext highlighter-rouge\\">void run(const volatile bool&amp; terminate)</code> 方法,该方法被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">...</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</title>\\n \\t<meta name=\\"description\\" content=\\"RTMFP 是 Adobe 开发的基于 UDP 协议的实时传输媒体流协议,支持 P2P 传输,具有较高的实时性和安全性。它的主要应用场景是视频通信、语音通信和网络游戏。OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。Cumulus 是一个基于 OpenRTMFP 的服务器,提供 RTMFP 服务。它具有轻量级、跨平台和可扩展的特点,并且还提供了负载均衡和可扩展性解决方案。YY 语音的 Web 端音视频流媒体能力,正是基于 RTMFP 协议做的迭代优化实现的。本文是船长关于这个系列文章的第一篇。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-09T18:57:19+00:00\\" class=\\"by-line\\">09 Apr 2012, 广州 | 作者 麦克船长 | 总计 8401 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfp是什么\\" id=\\"markdown-toc-一rtmfp是什么\\">一、RTMFP 是什么?</a> <ul>\\n <li><a href=\\"#文件分享-p2p-和实时流媒体-p2p-的区别是什么\\" id=\\"markdown-toc-文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</a></li>\\n <li><a href=\\"#rtmfp-和-rtmp-之间的区别是什么\\" id=\\"markdown-toc-rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</a></li>\\n <li><a href=\\"#flash-player-支持-rtmfp-吗\\" id=\\"markdown-toc-flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</a></li>\\n <li><a href=\\"#cumulus-使用-adobe-的-cirrus-key-吗\\" id=\\"markdown-toc-cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</a></li>\\n <li><a href=\\"#这个开源项目合法吗\\" id=\\"markdown-toc-这个开源项目合法吗\\">这个开源项目合法吗?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二openrtmfp和cumulus\\" id=\\"markdown-toc-二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</a></li>\\n <li><a href=\\"#三入门介绍与部署cumulusserver\\" id=\\"markdown-toc-三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</a> <ul>\\n <li><a href=\\"#1背景介绍\\" id=\\"markdown-toc-1背景介绍\\">1、背景介绍</a></li>\\n <li><a href=\\"#2准备工作\\" id=\\"markdown-toc-2准备工作\\">2、准备工作</a></li>\\n <li><a href=\\"#3安装\\" id=\\"markdown-toc-3安装\\">3、安装</a> <ul>\\n <li><a href=\\"#31外部依赖的安装\\" id=\\"markdown-toc-31外部依赖的安装\\">3.1、外部依赖的安装</a></li>\\n <li><a href=\\"#32安装openrtmfpcumulus\\" id=\\"markdown-toc-32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#4配置\\" id=\\"markdown-toc-4配置\\">4、配置</a></li>\\n <li><a href=\\"#5启动\\" id=\\"markdown-toc-5启动\\">5、启动</a></li>\\n <li><a href=\\"#6基本使用\\" id=\\"markdown-toc-6基本使用\\">6、基本使用</a></li>\\n <li><a href=\\"#7扩展cumulusserverserverapplication\\" id=\\"markdown-toc-7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三用lua编写helloworld应用扩展cumulusserver\\" id=\\"markdown-toc-三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</a> <ul>\\n <li><a href=\\"#1server-side\\" id=\\"markdown-toc-1server-side\\">1、Server-side</a> <ul>\\n <li><a href=\\"#11serverconfiguration\\" id=\\"markdown-toc-11serverconfiguration\\">1.1、Server configuration</a></li>\\n <li><a href=\\"#12applicationfile\\" id=\\"markdown-toc-12applicationfile\\">1.2、Application file</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2client-side\\" id=\\"markdown-toc-2client-side\\">2、Client-side</a></li>\\n <li><a href=\\"#3运行结果\\" id=\\"markdown-toc-3运行结果\\">3、运行结果</a></li>\\n <li><a href=\\"#4远程测试一个免费的测试服务器\\" id=\\"markdown-toc-4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<h3 id=\\"一rtmfp是什么\\">一、RTMFP 是什么?</h3>\\n\\n<p>Real-Time Media Flow Protocol(RTMFP)是 Adobe 开发的一种基于 UDP 并支持 P2P 的实时传输媒体流。主要特点是:高传输效率(可以使用压缩和算法来优化流量从而提高传输效率)、高实时性(可以保证媒体流的实时性使得视频通信和其他实时通信更加流畅)、支持 P2P 传输(减少对服务器的依赖从而减少带宽和服务器资源消耗)、高安全性(加密媒体流从而保证其安全性)。</p>\\n\\n<p>RTMFP 的主要应用场景包括:视频通信(视频聊天和视频会议)、语音通信(语音聊天、电话)、网络游戏。不过 RTMFP 目前仅有 Adobe 开发的版本,所以它并不是个开源项目,而是个商业化服务。那么有没有开源版本呢?</p>\\n\\n<h4 id=\\"文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</h4>\\n\\n<p>RTMFP 是一个 P2P 系统,但它仅针对实时通信时直接用户到用户之间的通信而设计,不能用于多个对等方之间进行文件共享(使用分段下载)。Facebook 在其 Pipe 应用中使用此协议将大文件直接在两个用户之间传输。</p>\\n\\n<h4 id=\\"rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</h4>\\n\\n<p>RTMP 是实时消息协议,RTMFP 代表实时媒体流协议。RTMFP 基于用户数据报协议(UDP),而 RTMP 基于传输控制协议(TCP)。\\n与 RTMP 不同,RTMFP 还支持直接从一个 Adobe Flash Player 传输数据到另一个,而无需经过服务器。</p>\\n\\n<h4 id=\\"flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</h4>\\n\\n<p>RTMFP 是基于用户数据报协议(UDP)的,而 RTMP 是基于传输控制协议(TCP)的。与 RTMP 不同,RTMFP 还支持直接在两个 Adobe Flash Player 之间发送数据,而不经过服务器。Flash Player 10.0 仅允许一对一通信进行 P2P,但从 10.1 开始允许应用程序级别的多播。Flash Player 查找适当的分发路由(覆盖网络),并可以将其分发到通过 P2P 连接的组。</p>\\n\\n<h4 id=\\"cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</h4>\\n\\n<p>不!当然,这是Cumulus的主要目标:成为Cirrus GPL的替代品。唯一的限制是:你的CPU,内存和单台机器的端口数。</p>\\n\\n<h4 id=\\"这个开源项目合法吗\\">这个开源项目合法吗?</h4>\\n\\n<p>在美国,数字千年版权法(Digital Millennium Copyright Act)规定,逆向工程用于协议互操作性是合法的。你可以在 WikiPedia 上查看相关讨论:</p>\\n\\n<ul>\\n <li>http://en.wikipedia.org/wiki/Real_Time_Media_Flow_Protocol</li>\\n <li>http://en.wikipedia.org/wiki/Proprietary_protocol</li>\\n <li>http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act</li>\\n</ul>\\n\\n<p>当逆向工程的目的是协议互操作性时,有法律先例。在美国,数字千年版权法(Digital Millennium Copyright Act)为逆向工程软件以使其与其他软件互操作提供了安全保障。</p>\\n\\n<h3 id=\\"二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</h3>\\n\\n<p>OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。它包含了 RTMFP 协议的实现,以及一些额外的功能,如媒体流传输、P2P 通信、脚本引擎和数据存储。</p>\\n\\n<p>Cumulus 是一个基于 OpenRTMFP 的服务器,是一个完整的开源且跨平台的 RTMFP 服务器,可通过脚本进行扩展。CumulusServer 根据 GPL 许可在考虑以下 4 个概念的情况下开发:速度、轻量、跨平台和可扩展。尽管尚未发布版本,但只有在 CumulusServer 经过测试和批准后才会将代码推送到 github。实际上,主要稳定的功能有:</p>\\n\\n<ul>\\n <li>P2P rendez-vous service</li>\\n <li>live streaming</li>\\n <li>RPC、pull、push exchange,实际上客户端和服务器之间的所有 AMF 可能交换</li>\\n <li>脚本引擎,用于创建自己的应用服务器或扩展 Cumulus 的功能</li>\\n <li>可扩展性和负载均衡解决方案</li>\\n</ul>\\n\\n<p>下面的内容是本篇 blog 的重点,包括两部分:先是 OpenRTMFP 应用的核心 CumulusServer 的入门介绍与部署,然后用 Lua 编写 HelloWorld 应用扩展 CumulusServer,我们开始吧!</p>\\n\\n<h3 id=\\"三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</h3>\\n\\n<h4 id=\\"1背景介绍\\">1、背景介绍</h4>\\n\\n<p>OpenRTMFP 可以帮助你实现 Flash 的实时应用的高并发扩展,OpenRTMFP/Cumulus 是基于 GNU General Public License 的。</p>\\n\\n<p>POCO:POrtable COmponents,是一个强大的开源 C++ 库。其在 C++ 开发中的角色,相当于 Java Class Library、苹果的 Cocoa、.NET framework。</p>\\n\\n<h4 id=\\"2准备工作\\">2、准备工作</h4>\\n\\n<p>下载:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><strong>External Dependencies</strong></th>\\n <th><strong>Official Site</strong></th>\\n <th><strong>Windows</strong></th>\\n <th><strong>Linux/OSX</strong></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>OpenSSL</td>\\n <td><a href=\\"http://www.slproweb.com/products/Win32OpenSSL.html\\">Official Site</a></td>\\n <td><a href=\\"http://www.slproweb.com/download/Win32OpenSSL_Light-1_0_1.exe\\">Download</a></td>\\n <td><a href=\\"http://www.openssl.org/source/openssl-1.0.1.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>Lua</td>\\n <td><a href=\\"http://www.lua.org/\\">Official Site</a></td>\\n <td><a href=\\"http://luaforwindows.googlecode.com/files/LuaForWindows_v5.1.4-45.exe\\">Download</a></td>\\n <td><a href=\\"http://www.lua.org/ftp/lua-5.1.5.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>POCO</td>\\n <td><a href=\\"http://pocoproject.org/\\">Official Site</a></td>\\n <td><a href=\\"http://downloads.sourceforge.net/project/poco/sources/poco-1.4.3/poco-1.4.3p1.zip\\">Download</a></td>\\n <td><a href=\\"https://sourceforge.net/projects/poco/files/sources/poco-1.4.3/poco-1.4.3p1.tar.gz/download\\">Download</a></td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>注意:POCO for linux 版本必须是 1.4.0 或更高,否则会引起 TCP 相关的 bug。</p>\\n\\n<h4 id=\\"3安装\\">3、安装</h4>\\n\\n<h5 id=\\"31外部依赖的安装\\">3.1、外部依赖的安装</h5>\\n\\n<p>Windows 下略,Linux 下基本就是:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>./configuremakesudo\\n<span class=\\"nv\\">$ </span>make <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</h5>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>OpenRTMFP-Cumulus/CumulusLib\\n<span class=\\"nv\\">$ </span>make\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd</span> ../CumulusServer\\n<span class=\\"nv\\">$ </span>make\\n</code></pre></div></div>\\n\\n<p>如果出现了 <code class=\\"language-plaintext highlighter-rouge\\">.h</code> 文件、lib 库找不到的情况,请修改 OpenRTMFP-Cumulus/CumulusLib/Makefile 或 OpenRTMFP-Cumulus/CumulusServer/Makefile。</p>\\n\\n<h4 id=\\"4配置\\">4、配置</h4>\\n\\n<p>通过编写 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP-Cumulus/CumulusServer/CumulusServer.ini</code> 文件来为 OpenRTMFP-Cumulus 进行个性化配置(默认是没有这个文件的),这个文件的内容形如:</p>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1985</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span>\\n<span class=\\"n\\">name</span><span class=\\"o\\">=</span><span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span><span class=\\"o\\">=</span><span class=\\"n\\">C</span><span class=\\"p\\">:</span><span class=\\"o\\">/</span><span class=\\"n\\">CumulusServer</span><span class=\\"o\\">/</span><span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<p>一些字段的设置含义如下,摘自:<a href=\\"https://github.com/OpenRTMFP/Cumulus/wiki/Installation\\">地址</a>。</p>\\n\\n<ul>\\n <li>公开给 Client 的端口号 <code class=\\"language-plaintext highlighter-rouge\\">port</code>,默认值是 1935(RTMFP 服务器的默认端口),用于 CumulusServer 监听 RTMFP 请求。</li>\\n <li>UDP 缓冲区字节数 <code class=\\"language-plaintext highlighter-rouge\\">udpBufferSize</code>, allows to change the size in bytes of UDP reception and sending buffer. Increases this value if your operating system has a default value too lower for important loads.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAliveServer</code>, time in seconds for periodically sending packets keep-alive with server, 15s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAlivePeer</code>, time in seconds for periodically sending packets keep-alive between peers, 10s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.activated</code>, activate or not the edges server on the RTMFP server (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, CumulusServer stays a RTMFP server without edges ability (default value is false).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.port</code>, port for the edges server, to accept incoming new CumulusEdge instances (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, it’s the port 1936.</li>\\n</ul>\\n\\n<blockquote>\\n <p>Warning: This port will receive plain text request from edges, for this purpose it should not be made public. It’s very important for security consideration. It must be available only for CumulusEdge instances, and anything else.</p>\\n</blockquote>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.attemptsBeforeFallback</code>, number of CumulusEdge attempt connections before falling back to CumulusServer (see CumulusEdge, Scalability page for more details about CumulusEdge). By default the value is 2 (in practical, 2 attempts happens after 5 sec approximately).</li>\\n <li>SMTP IP 地址 <code class=\\"language-plaintext highlighter-rouge\\">smtp.host</code>, configure a SMTP host to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is localhost.</li>\\n <li>SMTP 端口 <code class=\\"language-plaintext highlighter-rouge\\">smtp.port</code>, configure a SMTP port to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 25.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">smtp.timeout</code>, configure a SMTP timeout session in seconds to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 60 seconds.</li>\\n <li>日志路径 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code>,默认是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/logsby</code>。</li>\\n <li>日志文件名称 <code class=\\"language-plaintext highlighter-rouge\\">logs.name</code>,默认是<code class=\\"language-plaintext highlighter-rouge\\">log</code>。</li>\\n</ul>\\n\\n<h4 id=\\"5启动\\">5、启动</h4>\\n\\n<p>Windows 下的启动方法为:</p>\\n\\n<pre><code class=\\"language-dos\\">$ CumulusServer.exe /registerService [/displayName=CumulusServer /description=\\"Open Source RTMFP Server\\" /startup=automatic]\\n</code></pre>\\n\\n<p>Unix-like 下的启动方法为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"o\\">[</span><span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>/var/run/CumulusServer.pid]\\n</code></pre></div></div>\\n\\n<p>具体地,我的启动命令为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>./CumulusServer.pid\\n</code></pre></div></div>\\n\\n<h4 id=\\"6基本使用\\">6、基本使用</h4>\\n\\n<p>本地 Flash client 可以通过如下语句连接:</p>\\n\\n<div class=\\"language-as highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">$</span> <span class=\\"kd\\">var</span> <span class=\\"nx\\">nc</span><span class=\\"o\\">:</span><span class=\\"nx\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nx\\">NetConnection</span><span class=\\"p\\">()</span><span class=\\"o\\">;</span><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost/\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>RTMFP默认是采用1935端口,如果你特别指定了其他端口,比如12345,请使用如下方式:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>nc.connect(\\"rtmfp://localhost:12345/\\");\\n</code></pre></div></div>\\n\\n<h4 id=\\"7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</h4>\\n\\n<p>启动CumulusServer后,会在可执行文件的目录下出现一个www目录,该目录的作用,就是作为 Server Application 的默认根目录。具体的对应关系如下:</p>\\n\\n<blockquote>\\n <p>rtmfp://host:port/ -&gt; [CumulusServer folder]/www/main.lua (root application)</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/myApplication -&gt; [CumulusServer folder]/www/myApplication/main.lua</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/Games/myGame -&gt; [CumulusServer folder]/www/Games/myGame/main.lua</p>\\n</blockquote>\\n\\n<p>另外要提醒的是,如果main.lua文件被修改,则不需要重启 CumulusServer,因为 Server Application 的创建是一种动态的方式。</p>\\n\\n<h3 id=\\"三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</h3>\\n\\n<p>下面的这个实例是在本地(Client 与 Server 位于同一机器上)测试的。</p>\\n\\n<h4 id=\\"1server-side\\">1、Server-side</h4>\\n\\n<h5 id=\\"11serverconfiguration\\">1.1、Server configuration</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span> <span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1935</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span><span class=\\"n\\">name</span> <span class=\\"o\\">=</span> <span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12applicationfile\\">1.2、Application file</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">function</span> <span class=\\"nf\\">onConnection</span><span class=\\"p\\">(</span><span class=\\"n\\">client</span><span class=\\"p\\">,</span><span class=\\"n\\">response</span><span class=\\"p\\">,</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">function</span> <span class=\\"nf\\">client</span><span class=\\"p\\">:</span><span class=\\"n\\">test</span><span class=\\"p\\">(</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">firstname</span> <span class=\\"o\\">=</span> <span class=\\"n\\">unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">arg</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"s2\\">\\"Hello \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">firstname</span><span class=\\"o\\">..</span><span class=\\"s2\\">\\" \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">name</span>\\n <span class=\\"k\\">end</span>\\n <span class=\\"k\\">end</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2client-side\\">2、Client-side</h4>\\n\\n<div class=\\"language-java highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\">// CumulusClient.as</span>\\n\\n<span class=\\"kn\\">package</span> <span class=\\"err\\">{</span>\\n <span class=\\"nn\\">import</span> <span class=\\"n\\">flash</span><span class=\\"o\\">.</span><span class=\\"na\\">display</span><span class=\\"o\\">.</span><span class=\\"na\\">Sprite</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetConnection</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetStream</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.Responder</span><span class=\\"o\\">;</span>\\n\\n <span class=\\"kd\\">public</span> <span class=\\"kd\\">class</span> <span class=\\"nc\\">CumulusClient</span> <span class=\\"kd\\">extends</span> <span class=\\"nc\\">Sprite</span> <span class=\\"o\\">{</span>\\n <span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">nc:</span><span class=\\"nc\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t<span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">ns:</span><span class=\\"nc\\">NetStream</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t\\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">CumulusClient</span><span class=\\"o\\">()</span> <span class=\\"o\\">{</span>\\n <span class=\\"n\\">nc</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nc\\">NetConnection</span><span class=\\"o\\">();</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">connect</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"rtmfp://localhost\\"</span><span class=\\"o\\">);</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">client</span> <span class=\\"o\\">=</span> <span class=\\"k\\">this</span><span class=\\"o\\">;</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">call</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"test\\"</span><span class=\\"o\\">,</span><span class=\\"k\\">new</span> <span class=\\"nc\\">Responder</span><span class=\\"o\\">(</span><span class=\\"n\\">onResult</span><span class=\\"o\\">,</span><span class=\\"n\\">onStatus</span><span class=\\"o\\">),</span> <span class=\\"s\\">\\"OpenRTMFP/Cumulus\\"</span><span class=\\"o\\">,</span> <span class=\\"s\\">\\"World\\"</span><span class=\\"o\\">)</span>\\n <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">close</span><span class=\\"o\\">():</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span> \\n\\t\\t\\t<span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">close</span><span class=\\"o\\">();</span>\\n \\t<span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onStatus</span><span class=\\"o\\">(</span><span class=\\"nl\\">status:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">status</span><span class=\\"o\\">.</span><span class=\\"na\\">description</span><span class=\\"o\\">)</span>\\n\\t <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onResult</span><span class=\\"o\\">(</span><span class=\\"nl\\">response:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">response</span><span class=\\"o\\">)</span> <span class=\\"c1\\">// expected to display \\"Hello World OpenRTMFP/Cumulus\\" </span>\\n\\t <span class=\\"o\\">}</span> \\n\\t<span class=\\"o\\">}</span>\\n<span class=\\"o\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3运行结果\\">3、运行结果</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Hello World OpenRTMFP/Cumulus\\n[SWF] CumulusClient.swf - 解压缩后为 1,776 个字节\\n[卸装 SWF] CumulusClient.swf\\n</code></pre></div></div>\\n\\n<h4 id=\\"4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</h4>\\n\\n<p>获取 Developer Key 的地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/index.jsp\\n</code></pre></div></div>\\n\\n<p>服务器配置信息:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Server: amd64 OS: Linux 2.6.18-028stab095.1\\nServer IP: 108.59.252.39\\nOpenRTMFP as of: 22.Feb.2012\\n</code></pre></div></div>\\n\\n<p>编写服务器段应用地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/manage_ssls.jsp\\n</code></pre></div></div>\\n\\n<p>快去试试吧 :)</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"static_files":[{"basename":"KaTeX_AMS-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_AMS-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.ttf"},{"basename":"KaTeX_AMS-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_AMS-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.woff"},{"basename":"KaTeX_AMS-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_AMS-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.woff2"},{"basename":"KaTeX_Caligraphic-Bold","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.ttf"},{"basename":"KaTeX_Caligraphic-Bold","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.woff"},{"basename":"KaTeX_Caligraphic-Bold","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.woff2"},{"basename":"KaTeX_Caligraphic-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.ttf"},{"basename":"KaTeX_Caligraphic-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.woff"},{"basename":"KaTeX_Caligraphic-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Caligraphic-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.woff2"},{"basename":"KaTeX_Fraktur-Bold","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.ttf"},{"basename":"KaTeX_Fraktur-Bold","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.woff"},{"basename":"KaTeX_Fraktur-Bold","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.woff2"},{"basename":"KaTeX_Fraktur-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.ttf"},{"basename":"KaTeX_Fraktur-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.woff"},{"basename":"KaTeX_Fraktur-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Fraktur-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.woff2"},{"basename":"KaTeX_Main-Bold","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.ttf"},{"basename":"KaTeX_Main-Bold","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.woff"},{"basename":"KaTeX_Main-Bold","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.woff2"},{"basename":"KaTeX_Main-BoldItalic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-BoldItalic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.ttf"},{"basename":"KaTeX_Main-BoldItalic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-BoldItalic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.woff"},{"basename":"KaTeX_Main-BoldItalic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-BoldItalic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.woff2"},{"basename":"KaTeX_Main-Italic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.ttf"},{"basename":"KaTeX_Main-Italic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.woff"},{"basename":"KaTeX_Main-Italic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.woff2"},{"basename":"KaTeX_Main-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.ttf"},{"basename":"KaTeX_Main-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.woff"},{"basename":"KaTeX_Main-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Main-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.woff2"},{"basename":"KaTeX_Math-BoldItalic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-BoldItalic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.ttf"},{"basename":"KaTeX_Math-BoldItalic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-BoldItalic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.woff"},{"basename":"KaTeX_Math-BoldItalic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-BoldItalic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.woff2"},{"basename":"KaTeX_Math-Italic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.ttf"},{"basename":"KaTeX_Math-Italic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.woff"},{"basename":"KaTeX_Math-Italic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Math-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.woff2"},{"basename":"KaTeX_SansSerif-Bold","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.ttf"},{"basename":"KaTeX_SansSerif-Bold","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.woff"},{"basename":"KaTeX_SansSerif-Bold","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.woff2"},{"basename":"KaTeX_SansSerif-Italic","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.ttf"},{"basename":"KaTeX_SansSerif-Italic","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.woff"},{"basename":"KaTeX_SansSerif-Italic","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.woff2"},{"basename":"KaTeX_SansSerif-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.ttf"},{"basename":"KaTeX_SansSerif-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.woff"},{"basename":"KaTeX_SansSerif-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_SansSerif-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.woff2"},{"basename":"KaTeX_Script-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Script-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.ttf"},{"basename":"KaTeX_Script-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Script-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.woff"},{"basename":"KaTeX_Script-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Script-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.woff2"},{"basename":"KaTeX_Size1-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size1-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.ttf"},{"basename":"KaTeX_Size1-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size1-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.woff"},{"basename":"KaTeX_Size1-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size1-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.woff2"},{"basename":"KaTeX_Size2-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size2-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.ttf"},{"basename":"KaTeX_Size2-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size2-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.woff"},{"basename":"KaTeX_Size2-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size2-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.woff2"},{"basename":"KaTeX_Size3-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size3-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.ttf"},{"basename":"KaTeX_Size3-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size3-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.woff"},{"basename":"KaTeX_Size3-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size3-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.woff2"},{"basename":"KaTeX_Size4-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size4-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.ttf"},{"basename":"KaTeX_Size4-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size4-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.woff"},{"basename":"KaTeX_Size4-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Size4-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.woff2"},{"basename":"KaTeX_Typewriter-Regular","extname":".ttf","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Typewriter-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.ttf"},{"basename":"KaTeX_Typewriter-Regular","extname":".woff","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Typewriter-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.woff"},{"basename":"KaTeX_Typewriter-Regular","extname":".woff2","path":"/assets/plugins/katex.0.11.1/fonts/KaTeX_Typewriter-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.woff2"},{"basename":"katex.min","extname":".css","path":"/assets/plugins/katex.0.11.1/katex.min.css","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"katex.min.css"},{"basename":"syntax","extname":".css","path":"/css/syntax.css","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"syntax.css"},{"basename":"KaTeX_AMS-Regular","extname":".ttf","path":"/fonts/KaTeX_AMS-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.ttf"},{"basename":"KaTeX_AMS-Regular","extname":".woff","path":"/fonts/KaTeX_AMS-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.woff"},{"basename":"KaTeX_AMS-Regular","extname":".woff2","path":"/fonts/KaTeX_AMS-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_AMS-Regular.woff2"},{"basename":"KaTeX_Caligraphic-Bold","extname":".ttf","path":"/fonts/KaTeX_Caligraphic-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.ttf"},{"basename":"KaTeX_Caligraphic-Bold","extname":".woff","path":"/fonts/KaTeX_Caligraphic-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.woff"},{"basename":"KaTeX_Caligraphic-Bold","extname":".woff2","path":"/fonts/KaTeX_Caligraphic-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Bold.woff2"},{"basename":"KaTeX_Caligraphic-Regular","extname":".ttf","path":"/fonts/KaTeX_Caligraphic-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.ttf"},{"basename":"KaTeX_Caligraphic-Regular","extname":".woff","path":"/fonts/KaTeX_Caligraphic-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.woff"},{"basename":"KaTeX_Caligraphic-Regular","extname":".woff2","path":"/fonts/KaTeX_Caligraphic-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Caligraphic-Regular.woff2"},{"basename":"KaTeX_Fraktur-Bold","extname":".ttf","path":"/fonts/KaTeX_Fraktur-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.ttf"},{"basename":"KaTeX_Fraktur-Bold","extname":".woff","path":"/fonts/KaTeX_Fraktur-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.woff"},{"basename":"KaTeX_Fraktur-Bold","extname":".woff2","path":"/fonts/KaTeX_Fraktur-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Bold.woff2"},{"basename":"KaTeX_Fraktur-Regular","extname":".ttf","path":"/fonts/KaTeX_Fraktur-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.ttf"},{"basename":"KaTeX_Fraktur-Regular","extname":".woff","path":"/fonts/KaTeX_Fraktur-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.woff"},{"basename":"KaTeX_Fraktur-Regular","extname":".woff2","path":"/fonts/KaTeX_Fraktur-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Fraktur-Regular.woff2"},{"basename":"KaTeX_Main-Bold","extname":".ttf","path":"/fonts/KaTeX_Main-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.ttf"},{"basename":"KaTeX_Main-Bold","extname":".woff","path":"/fonts/KaTeX_Main-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.woff"},{"basename":"KaTeX_Main-Bold","extname":".woff2","path":"/fonts/KaTeX_Main-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Bold.woff2"},{"basename":"KaTeX_Main-BoldItalic","extname":".ttf","path":"/fonts/KaTeX_Main-BoldItalic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.ttf"},{"basename":"KaTeX_Main-BoldItalic","extname":".woff","path":"/fonts/KaTeX_Main-BoldItalic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.woff"},{"basename":"KaTeX_Main-BoldItalic","extname":".woff2","path":"/fonts/KaTeX_Main-BoldItalic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-BoldItalic.woff2"},{"basename":"KaTeX_Main-Italic","extname":".ttf","path":"/fonts/KaTeX_Main-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.ttf"},{"basename":"KaTeX_Main-Italic","extname":".woff","path":"/fonts/KaTeX_Main-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.woff"},{"basename":"KaTeX_Main-Italic","extname":".woff2","path":"/fonts/KaTeX_Main-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Italic.woff2"},{"basename":"KaTeX_Main-Regular","extname":".ttf","path":"/fonts/KaTeX_Main-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.ttf"},{"basename":"KaTeX_Main-Regular","extname":".woff","path":"/fonts/KaTeX_Main-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.woff"},{"basename":"KaTeX_Main-Regular","extname":".woff2","path":"/fonts/KaTeX_Main-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Main-Regular.woff2"},{"basename":"KaTeX_Math-BoldItalic","extname":".ttf","path":"/fonts/KaTeX_Math-BoldItalic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.ttf"},{"basename":"KaTeX_Math-BoldItalic","extname":".woff","path":"/fonts/KaTeX_Math-BoldItalic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.woff"},{"basename":"KaTeX_Math-BoldItalic","extname":".woff2","path":"/fonts/KaTeX_Math-BoldItalic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-BoldItalic.woff2"},{"basename":"KaTeX_Math-Italic","extname":".ttf","path":"/fonts/KaTeX_Math-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.ttf"},{"basename":"KaTeX_Math-Italic","extname":".woff","path":"/fonts/KaTeX_Math-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.woff"},{"basename":"KaTeX_Math-Italic","extname":".woff2","path":"/fonts/KaTeX_Math-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Math-Italic.woff2"},{"basename":"KaTeX_SansSerif-Bold","extname":".ttf","path":"/fonts/KaTeX_SansSerif-Bold.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.ttf"},{"basename":"KaTeX_SansSerif-Bold","extname":".woff","path":"/fonts/KaTeX_SansSerif-Bold.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.woff"},{"basename":"KaTeX_SansSerif-Bold","extname":".woff2","path":"/fonts/KaTeX_SansSerif-Bold.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Bold.woff2"},{"basename":"KaTeX_SansSerif-Italic","extname":".ttf","path":"/fonts/KaTeX_SansSerif-Italic.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.ttf"},{"basename":"KaTeX_SansSerif-Italic","extname":".woff","path":"/fonts/KaTeX_SansSerif-Italic.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.woff"},{"basename":"KaTeX_SansSerif-Italic","extname":".woff2","path":"/fonts/KaTeX_SansSerif-Italic.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Italic.woff2"},{"basename":"KaTeX_SansSerif-Regular","extname":".ttf","path":"/fonts/KaTeX_SansSerif-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.ttf"},{"basename":"KaTeX_SansSerif-Regular","extname":".woff","path":"/fonts/KaTeX_SansSerif-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.woff"},{"basename":"KaTeX_SansSerif-Regular","extname":".woff2","path":"/fonts/KaTeX_SansSerif-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_SansSerif-Regular.woff2"},{"basename":"KaTeX_Script-Regular","extname":".ttf","path":"/fonts/KaTeX_Script-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.ttf"},{"basename":"KaTeX_Script-Regular","extname":".woff","path":"/fonts/KaTeX_Script-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.woff"},{"basename":"KaTeX_Script-Regular","extname":".woff2","path":"/fonts/KaTeX_Script-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Script-Regular.woff2"},{"basename":"KaTeX_Size1-Regular","extname":".ttf","path":"/fonts/KaTeX_Size1-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.ttf"},{"basename":"KaTeX_Size1-Regular","extname":".woff","path":"/fonts/KaTeX_Size1-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.woff"},{"basename":"KaTeX_Size1-Regular","extname":".woff2","path":"/fonts/KaTeX_Size1-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size1-Regular.woff2"},{"basename":"KaTeX_Size2-Regular","extname":".ttf","path":"/fonts/KaTeX_Size2-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.ttf"},{"basename":"KaTeX_Size2-Regular","extname":".woff","path":"/fonts/KaTeX_Size2-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.woff"},{"basename":"KaTeX_Size2-Regular","extname":".woff2","path":"/fonts/KaTeX_Size2-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size2-Regular.woff2"},{"basename":"KaTeX_Size3-Regular","extname":".ttf","path":"/fonts/KaTeX_Size3-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.ttf"},{"basename":"KaTeX_Size3-Regular","extname":".woff","path":"/fonts/KaTeX_Size3-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.woff"},{"basename":"KaTeX_Size3-Regular","extname":".woff2","path":"/fonts/KaTeX_Size3-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size3-Regular.woff2"},{"basename":"KaTeX_Size4-Regular","extname":".ttf","path":"/fonts/KaTeX_Size4-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.ttf"},{"basename":"KaTeX_Size4-Regular","extname":".woff","path":"/fonts/KaTeX_Size4-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.woff"},{"basename":"KaTeX_Size4-Regular","extname":".woff2","path":"/fonts/KaTeX_Size4-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Size4-Regular.woff2"},{"basename":"KaTeX_Typewriter-Regular","extname":".ttf","path":"/fonts/KaTeX_Typewriter-Regular.ttf","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.ttf"},{"basename":"KaTeX_Typewriter-Regular","extname":".woff","path":"/fonts/KaTeX_Typewriter-Regular.woff","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.woff"},{"basename":"KaTeX_Typewriter-Regular","extname":".woff2","path":"/fonts/KaTeX_Typewriter-Regular.woff2","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"KaTeX_Typewriter-Regular.woff2"},{"basename":"avatar","extname":".jpg","path":"/img/about/avatar.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"avatar.jpg"},{"basename":"photo_1","extname":".jpg","path":"/img/about/photo_1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_1.jpg"},{"basename":"photo_10","extname":".jpg","path":"/img/about/photo_10.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_10.jpg"},{"basename":"photo_2","extname":".jpg","path":"/img/about/photo_2.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_2.jpg"},{"basename":"photo_3","extname":".jpg","path":"/img/about/photo_3.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_3.jpg"},{"basename":"photo_4","extname":".jpg","path":"/img/about/photo_4.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_4.jpg"},{"basename":"photo_5","extname":".jpg","path":"/img/about/photo_5.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_5.jpg"},{"basename":"photo_6","extname":".jpg","path":"/img/about/photo_6.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_6.jpg"},{"basename":"photo_7","extname":".JPG","path":"/img/about/photo_7.JPG","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_7.JPG"},{"basename":"photo_8","extname":".JPG","path":"/img/about/photo_8.JPG","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_8.JPG"},{"basename":"photo_9","extname":".jpg","path":"/img/about/photo_9.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"photo_9.jpg"},{"basename":"favicon","extname":".png","path":"/img/favicon.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"favicon.png"},{"basename":"img-test","extname":".png","path":"/img/img-test.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"img-test.png"},{"basename":"monochrome-mobile","extname":".png","path":"/img/monochrome-mobile.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"monochrome-mobile.png"},{"basename":"monochrome","extname":".svg","path":"/img/monochrome.svg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"monochrome.svg"},{"basename":"monochrome01","extname":".png","path":"/img/monochrome01.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"monochrome01.png"},{"basename":"2012-04-24-openrtmfp-cumulus-4-1","extname":".png","path":"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-04-24-openrtmfp-cumulus-4-1.png"},{"basename":"2012-04-24-openrtmfp-cumulus-4-2","extname":".png","path":"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-04-24-openrtmfp-cumulus-4-2.png"},{"basename":"2012-06-07-openrtmfp-cumulus-6-1","extname":".png","path":"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-07-openrtmfp-cumulus-6-1.png"},{"basename":"2012-06-07-openrtmfp-cumulus-6-2","extname":".png","path":"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-07-openrtmfp-cumulus-6-2.png"},{"basename":"2012-06-07-openrtmfp-cumulus-6-3","extname":".png","path":"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-07-openrtmfp-cumulus-6-3.png"},{"basename":"2012-06-25-openrtmfp-cumulus-7-1","extname":".png","path":"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-25-openrtmfp-cumulus-7-1.png"},{"basename":"2012-06-25-openrtmfp-cumulus-7-2","extname":".png","path":"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-25-openrtmfp-cumulus-7-2.png"},{"basename":"2012-06-25-openrtmfp-cumulus-7-3","extname":".png","path":"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2012-06-25-openrtmfp-cumulus-7-3.png"},{"basename":"2020-04-15-covid2019-catering-business-mode-1","extname":".jpg","path":"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-04-15-covid2019-catering-business-mode-1.jpg"},{"basename":"2020-06-11-captain-alibaba-1","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-1.png"},{"basename":"2020-06-11-captain-alibaba-2","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-2.png"},{"basename":"2020-06-11-captain-alibaba-3","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-3.png"},{"basename":"2020-06-11-captain-alibaba-4","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-4.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-4.png"},{"basename":"2020-06-11-captain-alibaba-5","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-5.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-5.png"},{"basename":"2020-06-11-captain-alibaba-6","extname":".png","path":"/img/src/2020-06-11-captain-alibaba-6.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-06-11-captain-alibaba-6.png"},{"basename":"2020-11-11-captain-double-eleven-1","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-1.jpg"},{"basename":"2020-11-11-captain-double-eleven-10","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-10.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-10.jpg"},{"basename":"2020-11-11-captain-double-eleven-11","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-11.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-11.jpg"},{"basename":"2020-11-11-captain-double-eleven-2","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-2.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-2.jpg"},{"basename":"2020-11-11-captain-double-eleven-3","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-3.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-3.jpg"},{"basename":"2020-11-11-captain-double-eleven-4","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-4.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-4.jpg"},{"basename":"2020-11-11-captain-double-eleven-5","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-5.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-5.jpg"},{"basename":"2020-11-11-captain-double-eleven-6","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-6.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-6.jpg"},{"basename":"2020-11-11-captain-double-eleven-7","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-7.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-7.jpg"},{"basename":"2020-11-11-captain-double-eleven-8","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-8.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-8.jpg"},{"basename":"2020-11-11-captain-double-eleven-9","extname":".jpg","path":"/img/src/2020-11-11-captain-double-eleven-9.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2020-11-11-captain-double-eleven-9.jpg"},{"basename":"2021-11-11-captain-tttm-1","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-1.jpg"},{"basename":"2021-11-11-captain-tttm-10","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-10.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-10.jpg"},{"basename":"2021-11-11-captain-tttm-11","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-11.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-11.jpg"},{"basename":"2021-11-11-captain-tttm-2","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-2.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-2.jpg"},{"basename":"2021-11-11-captain-tttm-3","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-3.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-3.jpg"},{"basename":"2021-11-11-captain-tttm-4","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-4.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-4.jpg"},{"basename":"2021-11-11-captain-tttm-5","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-5.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-5.jpg"},{"basename":"2021-11-11-captain-tttm-6","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-6.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-6.jpg"},{"basename":"2021-11-11-captain-tttm-7","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-7.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-7.jpg"},{"basename":"2021-11-11-captain-tttm-8","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-8.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-8.jpg"},{"basename":"2021-11-11-captain-tttm-9","extname":".jpg","path":"/img/src/2021-11-11-captain-tttm-9.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2021-11-11-captain-tttm-9.jpg"},{"basename":"2022-07-27-captain-birthday-1","extname":".png","path":"/img/src/2022-07-27-captain-birthday-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-07-27-captain-birthday-1.png"},{"basename":"2022-12-11-wechat-chatgpt-1","extname":".png","path":"/img/src/2022-12-11-wechat-chatgpt-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-11-wechat-chatgpt-1.png"},{"basename":"2022-12-11-wechat-chatgpt-2","extname":".png","path":"/img/src/2022-12-11-wechat-chatgpt-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-11-wechat-chatgpt-2.png"},{"basename":"2022-12-11-wechat-chatgpt-3","extname":".png","path":"/img/src/2022-12-11-wechat-chatgpt-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-11-wechat-chatgpt-3.png"},{"basename":"2022-12-16-midjourney-first-test-1","extname":".png","path":"/img/src/2022-12-16-midjourney-first-test-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-16-midjourney-first-test-1.png"},{"basename":"2022-12-16-midjourney-first-test-2","extname":".png","path":"/img/src/2022-12-16-midjourney-first-test-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-16-midjourney-first-test-2.png"},{"basename":"2022-12-16-midjourney-first-test-3","extname":".png","path":"/img/src/2022-12-16-midjourney-first-test-3.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-16-midjourney-first-test-3.png"},{"basename":"2022-12-17-ai-bert-1-1","extname":".jpg","path":"/img/src/2022-12-17-ai-bert-1-1.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-17-ai-bert-1-1.jpg"},{"basename":"2022-12-21-build-github-pages-with-jekyll-1","extname":".png","path":"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-21-build-github-pages-with-jekyll-1.png"},{"basename":"2022-12-21-build-github-pages-with-jekyll-2","extname":".png","path":"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-21-build-github-pages-with-jekyll-2.png"},{"basename":"2022-12-24-captain-nlp-1","extname":".png","path":"/img/src/2022-12-24-captain-nlp-1.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-1.png"},{"basename":"2022-12-24-captain-nlp-2","extname":".jpg","path":"/img/src/2022-12-24-captain-nlp-2.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-2.jpg"},{"basename":"2022-12-24-captain-nlp-3","extname":".jpg","path":"/img/src/2022-12-24-captain-nlp-3.jpg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-3.jpg"},{"basename":"2022-12-24-captain-nlp-4","extname":".png","path":"/img/src/2022-12-24-captain-nlp-4.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-4.png"},{"basename":"2022-12-24-captain-nlp-5","extname":".png","path":"/img/src/2022-12-24-captain-nlp-5.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-5.png"},{"basename":"2022-12-24-captain-nlp-6","extname":".png","path":"/img/src/2022-12-24-captain-nlp-6.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-6.png"},{"basename":"2022-12-24-captain-nlp-7","extname":".png","path":"/img/src/2022-12-24-captain-nlp-7.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-7.png"},{"basename":"2022-12-24-captain-nlp-8","extname":".png","path":"/img/src/2022-12-24-captain-nlp-8.png","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-24-captain-nlp-8.png"},{"basename":"2022-12-25-captain-cv-1","extname":".jpeg","path":"/img/src/2022-12-25-captain-cv-1.jpeg","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"2022-12-25-captain-cv-1.jpeg"},{"basename":"main","extname":".js","path":"/js/main.js","collection":null,"modified_time":"2023-01-03 19:13:24 +0000","name":"main.js"}],"categories":{"rt_tech":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 RTMFPServer 线程、RTMFPManager 对 RTMFPServer 的影响进行源码解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 9:关键线程逻辑分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-08-04T17:58:17+00:00\\" class=\\"by-line\\">04 Aug 2012, 广州 | 作者 麦克船长 | 总计 5236 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfpserver-线程的启动和等待\\" id=\\"markdown-toc-一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</a> <ul>\\n <li><a href=\\"#1pocothread\\" id=\\"markdown-toc-1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></a></li>\\n <li><a href=\\"#2封装一个可运行线程的类\\" id=\\"markdown-toc-2封装一个可运行线程的类\\">2、封装一个可运行线程的类</a></li>\\n <li><a href=\\"#3启动-rtmfpserver-线程\\" id=\\"markdown-toc-3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</a></li>\\n <li><a href=\\"#4rtmfpserver-线程等待\\" id=\\"markdown-toc-4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二rtmfpmanager-对-rtmfpserver-的影响\\" id=\\"markdown-toc-二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</a></li>\\n</ul>\\n\\n<h3 id=\\"一rtmfpserver-线程的启动和等待\\">一、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的启动和等待</h3>\\n\\n<h4 id=\\"1pocothread\\">1、<code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code></h4>\\n\\n<p>Cumulus 大量使用了 <code class=\\"language-plaintext highlighter-rouge\\">Poco</code> 的线程库。一个简单的 Poco 线程的使用实例如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PoechantRunnable</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// your codes</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">};</span>\\n \\n<span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">PoechantRunnable</span> <span class=\\"n\\">runnable</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Image that it's a gift</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">thread</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// And… thread is just like your girl</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">runnable</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Okay, give your sweet babe the gift :)</span>\\n <span class=\\"kr\\">thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2封装一个可运行线程的类\\">2、封装一个可运行线程的类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中实现了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 类,该类继承了 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,就是上面那个 <code class=\\"language-plaintext highlighter-rouge\\">gift</code> 喽。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">StartableProcess</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Runnable</span><span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">);</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">run</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>可以看到其中有 <code class=\\"language-plaintext highlighter-rouge\\">Startable&amp; _startable</code> 引用成员,它并没有继承 <code class=\\"language-plaintext highlighter-rouge\\">Runnable</code>,而是封装了 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Thread</span> <span class=\\"kr\\">_thread</span><span class=\\"p\\">;</span>\\n<span class=\\"n\\">StartableProcess</span> <span class=\\"n\\">_process</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>这里 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 封装了一个 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 成员,与 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 是有所区别的。接下俩我们看他们是怎么用的。</p>\\n\\n<h4 id=\\"3启动-rtmfpserver-线程\\">3、启动 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</h4>\\n<p>我们可以看到在 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的构造函数中初始化了 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 成员,初始化线程成员并传入线程名,设定标志域 <code class=\\"language-plaintext highlighter-rouge\\">(Flag Field)_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,因为它还没有调用启动函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_name</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_stop</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_process</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>初始化 <code class=\\"language-plaintext highlighter-rouge\\">_process</code> 时,调用 <code class=\\"language-plaintext highlighter-rouge\\">StartableProcess</code> 构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">StartableProcess</span><span class=\\"o\\">::</span><span class=\\"n\\">StartableProcess</span><span class=\\"p\\">(</span><span class=\\"n\\">Startable</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">startable</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_startable</span><span class=\\"p\\">(</span><span class=\\"n\\">startable</span><span class=\\"p\\">){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>传入 <code class=\\"language-plaintext highlighter-rouge\\">_startable</code> 的引用。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中所有的线程的可运行类都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的,然后通过调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 来启动,启动后会响应到 <code class=\\"language-plaintext highlighter-rouge\\">run()</code>。下面我们以 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程为例。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 类是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPServer</span>\\n <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Gateway</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">protected</span> <span class=\\"n\\">Handler</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">private</span> <span class=\\"n\\">SocketHandler</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的构造函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">RTMFPServer</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">cores</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">(</span><span class=\\"n\\">cores</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">(</span><span class=\\"n\\">_receivingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_sendingEngine</span><span class=\\"p\\">,</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中在初始化时调用了其父类的构造函数。接下来就要启动RTMFPServer线程了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>所在线程</th>\\n <th>调用者</th>\\n <th>函数</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>主线程</td>\\n <td>main(…)</td>\\n <td> </td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::start()</td>\\n </tr>\\n <tr>\\n <td>主线程</td>\\n <td>RTMFPServer从Startable继承来的Thread成员</td>\\n <td>Thread::start(…)</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象从Startable继承来的StartableProcess成员</td>\\n <td>StartableProcess::run()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>Startable::prerun()</td>\\n </tr>\\n <tr>\\n <td>RTMFPServer</td>\\n <td>RTMFPServer对象</td>\\n <td>RTMFPServer::run()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"4rtmfpserver-线程等待\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程等待</h4>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run()</code> 实现线程的持续运行,主要是依靠这两行代码:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"n\\">terminate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 函数很简单,如下只进行了 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">giveHandle()</code> 两个操作。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">handle</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">){</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">()</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">giveHandle</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的,声明如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">WakeUpType</span> <span class=\\"nf\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>在运行状态下,<code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,而默认参数 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code> 为 0,所以会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>这个 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员是一个 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 对象,<code class=\\"language-plaintext highlighter-rouge\\">Poco::Event</code> 有一个使用方式就是在调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::wait()</code> 后,会一直等待 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Event::set()</code> 被调用后,才会跳出 <code class=\\"language-plaintext highlighter-rouge\\">wait</code> 的状态。在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">set</code> 的动作是由:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::requestHandle()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">PoolThread::push(Poco::AutoPtr&lt;RunnableType&gt;&amp; pRunnable)</code></li>\\n</ul>\\n\\n<p>执行的。</p>\\n\\n<h3 id=\\"二rtmfpmanager-对-rtmfpserver-的影响\\">二、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的影响</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 同样,继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">RTMFPManager</span> <span class=\\"o\\">:</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Task</span><span class=\\"p\\">,</span> <span class=\\"k\\">private</span> <span class=\\"n\\">Startable</span>\\n</code></pre></div></div>\\n\\n<p>在构造函数中将 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 对象以引用方式传入,用以初始化其 <code class=\\"language-plaintext highlighter-rouge\\">_server</code> 引用成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">RTMFPManager</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">server</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_server</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Task</span><span class=\\"p\\">(</span><span class=\\"n\\">server</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">Startable</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPManager\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"cm\\">/* ...... */</span>\\n\\n<span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_server</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的构造函数中调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 成员函数,是从 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 继承而来的。然后会开启一个新的名为 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的线程。然后响应到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager::run()</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">PRIO_LOW</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">2000</span><span class=\\"p\\">)</span><span class=\\"o\\">!=</span><span class=\\"n\\">STOP</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">waitHandle</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里要强调的是,这里的 <code class=\\"language-plaintext highlighter-rouge\\">setPriority</code> 在 Linux 环境下会设置失败,可以参见我在 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 在 Github 上开启的 Issue #75,其中就包括这里的线程优先级设置。</p>\\n\\n<p>在这里我们可以看到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">handle(…)</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">sleep(…)</code> 是每 2 秒一次,而这是对 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程有影响的。还记得我说的 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程的 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent</code> 成员吗?(在第一部分中)它的激活就是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 中进行的,所以这里这个 2 秒是会影响到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 的主循环的等待时间的。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">timeout</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">WakeUpType</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">WAKEUP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">tryWait</span><span class=\\"p\\">(</span><span class=\\"n\\">timeout</span><span class=\\"p\\">))</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">TIMEOUT</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_wakeUpEvent</span><span class=\\"p\\">.</span><span class=\\"n\\">wait</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">STOP</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>你可以自行修改 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">sleep(...)</code> 的参数,这样就会调用 <code class=\\"language-plaintext highlighter-rouge\\">_wakeUpEvent.tryWait(timeout)</code> 了,按照指定的等待时间(即 <code class=\\"language-plaintext highlighter-rouge\\">timeout</code>)来进行睡眠。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 的作用是什么呢?核心就在于它的 <code class=\\"language-plaintext highlighter-rouge\\">handle</code> 成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handle</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_server</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这里就会调用到 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code>,所以你要在阅读 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 源码时知道 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::manage()</code> 函数并不是在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程内运行的,而是 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程内运行的。它的定义如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">manage</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>它实现对现有 Session 的一些管理,比如终止已经死掉的 <code class=\\"language-plaintext highlighter-rouge\\">Session</code>。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</title>\\n \\t<meta name=\\"description\\" content=\\"Flash 客户端通过 NetConnection 与 Cumulus 建立连接,然后通过 NetStream 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。本文将介绍如何经由服务器实现 Pub/Sub 流程。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 8:经由服务器的 Pub/Sub 流程的关键点</h2>\\t\\t\\n\\t<time datetime=\\"2012-07-23T03:07:43+00:00\\" class=\\"by-line\\">23 Jul 2012, 广州 | 作者 麦克船长 | 总计 3111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1客户端发布publishing-on-client-side\\" id=\\"markdown-toc-1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</a></li>\\n <li><a href=\\"#2服务器端server-side\\" id=\\"markdown-toc-2服务器端server-side\\">2、服务器端(Server-side)</a></li>\\n <li><a href=\\"#3客户端订阅subscribing-on-client-side\\" id=\\"markdown-toc-3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</a></li>\\n <li><a href=\\"#4reference\\" id=\\"markdown-toc-4reference\\">4、Reference</a></li>\\n</ul>\\n\\n<p>整个流程概括如下:</p>\\n\\n<p>Flash 客户端通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 建立连接,然后通过 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 使用 RTMFP 发布 Audio/Video/Data(下面简称为 A/V/D) 给服务器,这个 Flash Player 就作为一个发布者(Publisher)。RTMFP 服务器接收到后给所有的订阅者(Subscribers)发送 Audio/Video/Data。</p>\\n\\n<h3 id=\\"1客户端发布publishing-on-client-side\\">1、客户端发布(Publishing on client side)</h3>\\n\\n<p>通过 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 连接 RTMFP 服务器 Cumulus,可以参考<a href=\\"/2012/04/10/openrtmfp-cumulus-1/\\">《OpenRTMFP/Cumulus 原理及源码解读 1:入门介绍、部署与 Hello World》</a>一文。关键的一个语句如下,其中 <code class=\\"language-plaintext highlighter-rouge\\">nc</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost:1935\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>在连接成功后通过 NetStream 发布 Audio/Video,如下所示,其中 <code class=\\"language-plaintext highlighter-rouge\\">ns1</code> 是一个 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 对象。</p>\\n\\n<div class=\\"language-actionscript highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">ns1</span><span class=\\"p\\">.</span><span class=\\"nx\\">publish</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"poechant_media_flow\\"</span><span class=\\"p\\">,</span> <span class=\\"s2\\">\\"live\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>根据音视频不同的需求,播放相应内容。如果是发布 Data,则使用NetStream.send()来实现。这样就完成了客户端的 A/V/D 发布</p>\\n\\n<h3 id=\\"2服务器端server-side\\">2、服务器端(Server-side)</h3>\\n\\n<p>Cumulus 通过 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPReceiving</code> 这个 RTMFP 协议数据接收引擎完成一些连接建立的相关动作,以及接收数据包:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPReceiving</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">rtmfpReceiving</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数会在收到客户端发来请求时响应,如果是仍未建立连接的请求,则由此创建 Session(RTMFP 的核心概念之一),并取出其中的数据包。这其中有多个过程,我这里就不详述,以后会发布文章来解释。</p>\\n\\n<p>继续我们的话题,在RTMFPServer::receive 函数中如果是建立连接阶段,则会调用 <code class=\\"language-plaintext highlighter-rouge\\">Handshake</code> 类的 <code class=\\"language-plaintext highlighter-rouge\\">receive</code> 来做接下来的处理,这个我就不去详细分析了,因为与本文主题无关。与本文有关的是,如果是已经创建了 Session 的,则会调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ServerSession</span><span class=\\"o\\">::</span><span class=\\"n\\">packetHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是一个相对复杂的函数,会从 packet 中取出很多有用的信息。此外,比较重要的是,在我们上述情况下,会调用 Flow 类的:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Flow</span><span class=\\"o\\">::</span><span class=\\"n\\">fragmentSortedHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">stage</span><span class=\\"p\\">,</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">fragment</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">flags</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>该函数中会对 Audio/Video/Data 分别响应不同的处理机制:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF_WITH_HANDLER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AMF</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">messageHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">amf</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">AUDIO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">audioHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">Message</span><span class=\\"o\\">::</span><span class=\\"n\\">VIDEO</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">videoHandler</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">rawHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>接下来在 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中完成对所有订阅了该发布者的 Flash Players 发送信息,核心的代码为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushAudioPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushVideoPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">time</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"k\\">for</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span><span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_listeners</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span><span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pushDataPacket</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">packet</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中的 <code class=\\"language-plaintext highlighter-rouge\\">_listeners</code> 就是该 <code class=\\"language-plaintext highlighter-rouge\\">Publication</code> 中的所有订阅者。订阅者的添加/删除是通过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Listener</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">addListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">writer</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">unbuffered</span><span class=\\"p\\">);</span>\\n \\n<span class=\\"kt\\">void</span> <span class=\\"nf\\">removeListener</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Peer</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">peer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">id</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这两个函数来实现的。</p>\\n\\n<p>要注意的是,在 Publication 中已经完成了向订阅者发布信息,之后虽然会响应到 Peer 及 RTMFPServer 的onAudioPacket、onVideoPacket、onDataPacket,但此时都与订阅者接收信息无关了。Cumulus 正是在RTMFPServer::onAudioPacket、RTMFPServer::onVideoPacket、RTMFPServer::onDataPacket中调用用户定制的服务(Lua 脚本实现),完成一些自定义的需求。我是在此通过直接的 C++ 功能扩展,来添加业务需求的,没有使用 Lua 脚本及 Cumulus 中的 Lua 脚本引擎,主要原因是为了提高效率。</p>\\n\\n<h3 id=\\"3客户端订阅subscribing-on-client-side\\">3、客户端订阅(Subscribing on client side)</h3>\\n\\n<p>订阅很简单,在 play 的时候传入正确的发布者名称即可。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>ns2.play(\\"poechant_media_flow\\");\\n</code></pre></div></div>\\n\\n<p>测试代码可以参考 Reference-1,其中的例子是关于 <code class=\\"language-plaintext highlighter-rouge\\">NetStream::send(…)</code> 的,用于发送 <code class=\\"language-plaintext highlighter-rouge\\">Data</code>,<code class=\\"language-plaintext highlighter-rouge\\">Audio</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">Video</code> 的程序可以参考该例修改。</p>\\n\\n<p>客户端订阅后,这些信息并不会直接从发布者那里通过 P2P 的方式接收。如果想使用发布者与接受者直接连接的方式,则需要在 <code class=\\"language-plaintext highlighter-rouge\\">NetStream</code> 初始化的时候,传入 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.DIRECT_CONNECTIONS</code> 参数,默认的 <code class=\\"language-plaintext highlighter-rouge\\">NetStream.CONNECT_TO_FMS</code> 是将数据上行到服务器再下行给所有订阅者(Subscribers)的。根据不同的应用场景,可以使用不同的方式。</p>\\n\\n<h3 id=\\"4reference\\">4、Reference</h3>\\n\\n<ul>\\n <li>http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#send()</li>\\n</ul>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。Cumulus 启动后,我们可以看到有多个线程被创建,但是有时其中的个别线程没有被成功启动,本文将告诉你如何修复并解决。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 7:Cumulus 源码的一个线程启动 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-25T02:56:26+00:00\\" class=\\"by-line\\">25 Jun 2012, 广州 | 作者 麦克船长 | 总计 2111 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 中的线程都是继承自 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code>,在其中封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Thread</code> 成员,使得一些有关线程的操作更方便。<code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 函数如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样一个类继承 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的话,并启动时传入自己,则会调用到 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code>,然后调用到该类自己的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数。一般来说这个函数会一个循环,以 <code class=\\"language-plaintext highlighter-rouge\\">SocketManager</code> 为例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">SocketManager</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span> \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"err\\">…</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>我们要看看这个 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 是怎么回事,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">running</span><span class=\\"p\\">()</span> <span class=\\"k\\">const</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>很简单,就是通过 <code class=\\"language-plaintext highlighter-rouge\\">Startable::_stop</code> 成员来判断是否还需要继续循环下去。那么这个 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 是什么时候被设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 的呢?就是上面的 <code class=\\"language-plaintext highlighter-rouge\\">start()</code>,这里存在的一个问题就是先 <code class=\\"language-plaintext highlighter-rouge\\">start</code> 线程,再设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_thread.start(_process);\\n_stop=false;\\n</code></pre></div></div>\\n\\n<p>而 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 之后 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 的时候就开始通过 <code class=\\"language-plaintext highlighter-rouge\\">running()</code> 来判断 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值了。所以你会在使用 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus</code> 时,发现有时候启动起来的线程个数不对。正常情况下应该有四个线程:</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>它们是:</p>\\n\\n<ul>\\n <li>主线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 线程</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 线程</li>\\n</ul>\\n\\n<p>而异常情况可能是 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动,甚至 <code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 没有启动的情况,这时客户端是无法接入成功的。</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-2.png\\" alt=\\"image\\" /></p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MainSockets</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPManager</code> 都没有启动的情况 T.T</p>\\n\\n<p><img src=\\"/img/src/2012-06-25-openrtmfp-cumulus-7-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>具体是哪个线程没有启动成功可以通过 GDB 查看。</p>\\n\\n<p>解决办法就是将 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 的设置操作,在启动线程之前。不过要注意锁要同时移动,并且在产生异常时设置 <code class=\\"language-plaintext highlighter-rouge\\">_stop</code> 值为 <code class=\\"language-plaintext highlighter-rouge\\">true</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_stop</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// if running</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Try to start up a new thread inherited from Startable\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span><span class=\\"o\\">=</span>\\n <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">_process</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> \\n <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span>\\n \\n <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutexStop</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_stop</span> <span class=\\"o\\">=</span> \\n <span class=\\"nb\\">true</span><span class=\\"p\\">;</span> \\n <span class=\\"c1\\">// June 25th, 2012, Michael@YY</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"Impossible to start the thread : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。对于使用 Cumulus 来做二次开发的技术人员,CumulusLib 是一定会使用到的,但是 CumulusLib 的源码在被单独使用时是存在严重的线程安全 Bug 的,这就是本文诞生的原因。YY 的网页版流媒体技术服务端使用到 CumulusLib 时遇到了这个问题,因此修复了这个 Bug。最终的 Bug 修复很简单,但是要先理解 CumulusLib 整体线程安全问题才能确定解决方案。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 6:独立使用 CumulusLib 源码时的线程安全 Bug 及修复方法</h2>\\t\\t\\n\\t<time datetime=\\"2012-06-07T15:34:18+00:00\\" class=\\"by-line\\">07 Jun 2012, 广州 | 作者 麦克船长 | 总计 1538 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>OpenRTMFP/Cumulus 提供了 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 可以供其他 RTMFP 应用使用,而不局限于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>。</p>\\n\\n<p>一般来说,Thread A 会准备好要 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 的消息,然后 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息。</p>\\n\\n<p>但是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 中实现的,是 Thread A 向消息队列 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 消息,然后根据这个消息在队列中的指针,再向消息内填写字段。并期望如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>由于在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中,一个 Client 只在一个线程内被操作,相应的 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 也不会出现跨线程的问题。但是如果单独使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code>,如果出现线程通信,并且共享 <code class=\\"language-plaintext highlighter-rouge\\">FlowWriter</code> 的话,就会共享消息队列,此时可能出现这种情况。</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就导致了很严重的错误,会使得进程崩溃。修正的方式,可以是将消息完全准备好之后,再放入队列,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">createAMFMessage</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">)</span>\\n \\n <span class=\\"c1\\">// signature.empty() means that we are on the flowWriter of FlowNull</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"p\\">(</span><span class=\\"n\\">_closed</span> <span class=\\"o\\">||</span> <span class=\\"n\\">signature</span><span class=\\"p\\">.</span><span class=\\"n\\">empty</span><span class=\\"p\\">()</span> <span class=\\"o\\">||</span> <span class=\\"n\\">_band</span><span class=\\"p\\">.</span><span class=\\"n\\">failed</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">MessageBuffered</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">message</span><span class=\\"p\\">(</span><span class=\\"n\\">_MessageNull</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeResponseHeader</span><span class=\\"p\\">(</span><span class=\\"n\\">message</span><span class=\\"p\\">.</span><span class=\\"n\\">rawWriter</span><span class=\\"p\\">,</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后再调用时最后再增加 <code class=\\"language-plaintext highlighter-rouge\\">push</code> 操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就使得消息的数据被写完了,才被放入队列中,如下:</p>\\n\\n<p><img src=\\"/img/src/2012-06-07-openrtmfp-cumulus-6-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>不过如果考虑线程安全,多个线程对同一个消息队列进行操作时,就要加锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cm\\">/*\\n * author: michael\\n * date: June 6th, 2012\\n * type: add\\n */</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">FlowWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">pushAMFMessage</span><span class=\\"p\\">(</span><span class=\\"n\\">MessageBuffered</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMessage</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span> <span class=\\"o\\">!=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Mutex</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedLock</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">msgQueueMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_messages</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pMessage</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这样就基本解决了这个线程安全问题。</p>\\n\\n<p>另外,使用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusLib</code> 要遵循 GPL 协议,一定不要忘记。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 5:IO 管理源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T03:31:10+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 12668 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一流缓冲区\\" id=\\"markdown-toc-一流缓冲区\\">一、流缓冲区</a> <ul>\\n <li><a href=\\"#1了解-stdstreambuf\\" id=\\"markdown-toc-1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></a> <ul>\\n <li><a href=\\"#11单步移动内置指针\\" id=\\"markdown-toc-11单步移动内置指针\\">1.1、单步移动内置指针</a></li>\\n <li><a href=\\"#12获取-get-指针和-put-指针的位置\\" id=\\"markdown-toc-12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</a></li>\\n <li><a href=\\"#13设置-get-和-put-指针可达区域的上下界\\" id=\\"markdown-toc-13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2memorystreambuf\\" id=\\"markdown-toc-2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></a> <ul>\\n <li><a href=\\"#21移动内置的-get-和-put-指针\\" id=\\"markdown-toc-21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</a></li>\\n <li><a href=\\"#22获取-get-和-put-指针当前位置\\" id=\\"markdown-toc-22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</a></li>\\n <li><a href=\\"#23获取缓冲区的起始位置和大小\\" id=\\"markdown-toc-23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</a></li>\\n <li><a href=\\"#24缓冲区的已写字节数\\" id=\\"markdown-toc-24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</a></li>\\n <li><a href=\\"#25显式设定-put-和-get-指针位置\\" id=\\"markdown-toc-25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</a></li>\\n <li><a href=\\"#26-修改缓冲区大小\\" id=\\"markdown-toc-26-修改缓冲区大小\\">2.6 修改缓冲区大小</a></li>\\n <li><a href=\\"#27构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二io-流\\" id=\\"markdown-toc-二io-流\\">二、IO 流</a> <ul>\\n <li><a href=\\"#1了解-stdios\\" id=\\"markdown-toc-1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></a></li>\\n <li><a href=\\"#2memoryios\\" id=\\"markdown-toc-2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></a> <ul>\\n <li><a href=\\"#21构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#22得到-memorystreambuf-成员的地址\\" id=\\"markdown-toc-22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</a></li>\\n <li><a href=\\"#23当前位置\\" id=\\"markdown-toc-23当前位置\\">2.3、当前位置</a></li>\\n <li><a href=\\"#24封装-memorystreambuf-成员的一些函数\\" id=\\"markdown-toc-24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</a></li>\\n <li><a href=\\"#25-缓冲区可读数据的字节数\\" id=\\"markdown-toc-25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3输入流\\" id=\\"markdown-toc-3输入流\\">3、输入流</a></li>\\n <li><a href=\\"#4输出流\\" id=\\"markdown-toc-4输出流\\">4、输出流</a> <ul>\\n <li><a href=\\"#41-构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</a></li>\\n <li><a href=\\"#42-读取和设定已写字节数\\" id=\\"markdown-toc-42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</a></li>\\n <li><a href=\\"#43-当前位置\\" id=\\"markdown-toc-43-当前位置\\">4.3 当前位置</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#三局部内存片\\" id=\\"markdown-toc-三局部内存片\\">三、局部内存片</a> <ul>\\n <li><a href=\\"#1构造函数\\" id=\\"markdown-toc-1构造函数\\">1、构造函数</a></li>\\n <li><a href=\\"#2析构函数\\" id=\\"markdown-toc-2析构函数\\">2、析构函数</a></li>\\n <li><a href=\\"#3缓冲区切割\\" id=\\"markdown-toc-3缓冲区切割\\">3、缓冲区切割</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 Cumulus 中 Input/Output 管理的源码分析,包括流缓冲区、IO 流、局部内存片。</p>\\n\\n<h3 id=\\"一流缓冲区\\">一、流缓冲区</h3>\\n\\n<p>这段我们主要分析 MemoryStream.h 文件中定义的类。</p>\\n\\n<h4 id=\\"1了解-stdstreambuf\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::streambuf</code></h4>\\n\\n<p>首先要了解 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 内置了一个 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针和一个 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针。<code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的所有操作基本都是对这两个指针的操作。其一些成员函数的缩写中的 <code class=\\"language-plaintext highlighter-rouge\\">g</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">p</code> 就分别表示 get pointer 和 put pointer。</p>\\n\\n<h5 id=\\"11单步移动内置指针\\">1.1、单步移动内置指针</h5>\\n\\n<p>Increase get pointer: Advances the get pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The get pointer is the internal pointer that points to the next location in the controlled input sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">gbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>Increase put pointer: Advances the put pointer by <code class=\\"language-plaintext highlighter-rouge\\">n</code> positions. The put pointer is the internal pointer that points to the next location of the controlled output sequence.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">pbump</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">n</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12获取-get-指针和-put-指针的位置\\">1.2、获取 get 指针和 put 指针的位置</h5>\\n\\n<p>Pointer to current position of input sequence: Returns a reference to the current element of the controlled input sequence (i.e., the “get pointer”).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">gptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>Pointer to current position of output sequence: Returns a reference to the current element of the output sequence (the put pointer).</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span> <span class=\\"n\\">pptr</span> <span class=\\"p\\">(</span> <span class=\\"p\\">)</span> <span class=\\"k\\">const</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"13设置-get-和-put-指针可达区域的上下界\\">1.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针可达区域的上下界</h5>\\n\\n<p>Set input sequence pointers: Sets values for the pointers that define both the boundaries of the accessible part of the controlled input sequence and the get pointer itself.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setg</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gnext</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled input sequence.\\ngnext: New value for the get pointer, which points to the next element within the controlled input sequence where the next input operation shall be performed.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">gend</code>: New value for the end pointer, just past the end of the accessible part of the controlled input sequence.</li>\\n <li>Set output sequence pointers: Sets the values that define the boundaries of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">setp</span> <span class=\\"p\\">(</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pbeg</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pend</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pbeg</code>: New value for the pointer to the beginning of the accessible part of the controlled output sequenceand put pointer.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">pend</code>: New value for the end pointer, just past the end of the accessible part of the controlled output sequence.</li>\\n</ul>\\n\\n<h4 id=\\"2memorystreambuf\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code></h4>\\n\\n<p>类定义:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryStreamBuf</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">streambuf</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">friend</span> <span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span><span class=\\"p\\">;</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Explaint below</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">overflow</span><span class=\\"p\\">(</span><span class=\\"n\\">int_type</span> <span class=\\"n\\">c</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">underflow</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">int</span> <span class=\\"n\\">sync</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"k\\">operator</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的友元,其内部有 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 的成员,这里暂且不管。构造函数传入的参数是缓冲区的地址和缓冲区大小(字节数)。拷贝构造函数和析构函数不必赘述。</p>\\n\\n<h5 id=\\"21移动内置的-get-和-put-指针\\">2.1、移动内置的 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 指针:</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针都移动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">gbump</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22获取-get-和-put-指针当前位置\\">2.2、获取 get 和 put 指针当前位置:</h5>\\n\\n<p>封装 <code class=\\"language-plaintext highlighter-rouge\\">streambuf</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">gptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">pptr</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23获取缓冲区的起始位置和大小\\">2.3、获取缓冲区的起始位置和大小:</h5>\\n\\n<p>依赖于内置成员变量 pBuffer 和 bufferSize:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24缓冲区的已写字节数\\">2.4、缓冲区的已写字节数</h5>\\n\\n<p>读取(其中也可能发生设置操作):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// 已写字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">written</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_written</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_written</span><span class=\\"o\\">=</span><span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25显式设定-put-和-get-指针位置\\">2.5、显式设定 <code class=\\"language-plaintext highlighter-rouge\\">put</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">get</code> 指针位置</h5>\\n\\n<p>设定 put 和 get 指针为以缓冲区首地址为开始偏移量为 pos 的位置:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 保存已写字节数</span>\\n <span class=\\"n\\">written</span><span class=\\"p\\">();</span> <span class=\\"c1\\">// Save nb char written</span>\\n \\n <span class=\\"c1\\">// 移动 put 指针</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 移动 get 指针</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"26-修改缓冲区大小\\">2.6 修改缓冲区大小</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// 大小标识</span>\\n <span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// gptr 当前位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">gCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">pos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span> \\n <span class=\\"c1\\">// pptr 当前位置</span>\\n <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pos</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span> <span class=\\"n\\">pos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达范围和当前位置</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"27构造函数拷贝构造函数和析构函数\\">2.7、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>构造函数会设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">bufferSize</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数会拷贝对方的 <code class=\\"language-plaintext highlighter-rouge\\">pBuffer</code>、<code class=\\"language-plaintext highlighter-rouge\\">bufferSizse</code>、<code class=\\"language-plaintext highlighter-rouge\\">_written</code>,并设定 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>、<code class=\\"language-plaintext highlighter-rouge\\">pptr</code>。注意设定 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 时,要分别调用 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">pbump</code>,因为 <code class=\\"language-plaintext highlighter-rouge\\">setp</code> 仅将 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 设定为传入的首个参数值(与可达范围的首地址相同)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> <span class=\\"n\\">_pBuffer</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">),</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">),</span><span class=\\"n\\">_written</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">(),</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pbump</span><span class=\\"p\\">((</span><span class=\\"kt\\">int</span><span class=\\"p\\">)(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">));</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二io-流\\">二、IO 流</h3>\\n\\n<h4 id=\\"1了解-stdios\\">1、了解 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code></h4>\\n\\n<p>Initialize object [<code class=\\"language-plaintext highlighter-rouge\\">protected</code>]: This protected member initializes the values of the stream’s internal flags and member variables.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"nf\\">init</span> <span class=\\"p\\">(</span> <span class=\\"n\\">streambuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">sb</span> <span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>初始化后如下函数的返回值:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th>member function</th>\\n <th>value</th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>rdbuf()</td>\\n <td>sb</td>\\n </tr>\\n <tr>\\n <td>tie()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>rdstate()</td>\\n <td>goodbit if sb is not a null pointer, badbit otherwise</td>\\n </tr>\\n <tr>\\n <td>exceptions()</td>\\n <td>goodbit</td>\\n </tr>\\n <tr>\\n <td>flags()</td>\\n <td>skipws | dec</td>\\n </tr>\\n <tr>\\n <td>width()</td>\\n <td>0</td>\\n </tr>\\n <tr>\\n <td>precision()</td>\\n <td>6</td>\\n </tr>\\n <tr>\\n <td>fill()</td>\\n <td>‘ ’ (whitespace)</td>\\n </tr>\\n <tr>\\n <td>getloc()</td>\\n <td>a copy of locale()</td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<h4 id=\\"2memoryios\\">2、<code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code>,且是 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code>的基类,用以确保流缓冲区和基类的初始化序列的正确性。该类继承自 <code class=\\"language-plaintext highlighter-rouge\\">std::ios</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryIOS</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"k\\">virtual</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ios</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryStreamBuf</span> <span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数拷贝构造函数和析构函数\\">2.1、构造函数、拷贝构造函数和析构函数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">poco_ios_init</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">init</code> 的宏定义,用于初始化成员 <code class=\\"language-plaintext highlighter-rouge\\">_buf</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span><span class=\\"n\\">_buf</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_buf</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">poco_ios_init</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>拷贝构造函数同构造函数。如下的析构函数不必赘述:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22得到-memorystreambuf-成员的地址\\">2.2、得到 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的地址</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">&amp;</span><span class=\\"n\\">_buf</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23当前位置\\">2.3、当前位置</h5>\\n\\n<p>这是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutpuStream</code> 继承时实现:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24封装-memorystreambuf-成员的一些函数\\">2.4、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newSize</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">newSize</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code> 封装为 <code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">position</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"25-缓冲区可读数据的字节数\\">2.5 缓冲区可读数据的字节数</h5>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"p\\">(</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">begin</span><span class=\\"p\\">());</span> <span class=\\"c1\\">// 缓冲区剩余可读数据字节数</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">result</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3输入流\\">3、输入流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryInputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryInputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for reading.</span>\\n <span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造函数、拷贝构造函数和析构函数也都没什么可说的,初始化 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 以及 <code class=\\"language-plaintext highlighter-rouge\\">istream</code>。<code class=\\"language-plaintext highlighter-rouge\\">istream</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">iostream</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">basic_istream</code> 别名(<code class=\\"language-plaintext highlighter-rouge\\">typedef</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"k\\">const_cast</span><span class=\\"o\\">&lt;</span><span class=\\"kt\\">char</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">),</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">istream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryInputStream</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>唯一的一个成员函数是 <code class=\\"language-plaintext highlighter-rouge\\">current</code>,封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryIOS</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员的 <code class=\\"language-plaintext highlighter-rouge\\">gCurrent</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryInputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4输出流\\">4、输出流</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">MemoryOutputStream</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">,</span> <span class=\\"k\\">public</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span>\\n <span class=\\"c1\\">/// An input stream for reading from a memory area.</span>\\n<span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"c1\\">/// Creates a MemoryOutputStream for the given memory area,</span>\\n <span class=\\"c1\\">/// ready for writing.</span>\\n <span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">();</span>\\n <span class=\\"c1\\">/// Destroys the MemoryInputStream.</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"41-构造函数拷贝构造函数和析构函数\\">4.1 构造函数、拷贝构造函数和析构函数</h5>\\n\\n<p>如下,不赘述了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span> \\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">bufferSize</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">MemoryIOS</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span> <span class=\\"n\\">ostream</span><span class=\\"p\\">(</span><span class=\\"n\\">rdbuf</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::~</span><span class=\\"n\\">MemoryOutputStream</span><span class=\\"p\\">(){</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"42-读取和设定已写字节数\\">4.2 读取和设定已写字节数</h5>\\n\\n<p>读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"43-当前位置\\">4.3 当前位置</h5>\\n\\n<p>与 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 中的封装类似:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">MemoryOutputStream</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">rdbuf</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三局部内存片\\">三、局部内存片</h3>\\n\\n<p>在第一部分的流缓冲区介绍 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 时,其中有一个名为 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的友元,它就是本文所要介绍的。首先,最重要的是,<code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 中有一个 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStreamBuf</code> 成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ScopedMemoryClip</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"1构造函数\\">1、构造函数</h4>\\n\\n<p>构造函数传入的参数对应的就是 <code class=\\"language-plaintext highlighter-rouge\\">ScopedMemoryClip</code> 的两个成员值。其中偏移量不能超过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryStremamBuf</code> 的缓冲区上线值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">(</span><span class=\\"n\\">MemoryStreamBuf</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_offset</span><span class=\\"p\\">(</span><span class=\\"n\\">offset</span><span class=\\"p\\">),</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">(</span><span class=\\"n\\">buffer</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&gt;=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_offset</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_offset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2析构函数\\">2、析构函数</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::~</span><span class=\\"n\\">ScopedMemoryClip</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">_offset</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3缓冲区切割\\">3、缓冲区切割</h4>\\n\\n<p>可以看到构造函数和析构函数中都调用了 <code class=\\"language-plaintext highlighter-rouge\\">clip</code> 函数,该函数切割完缓冲区,形成局部内存片:</p>\\n\\n<ul>\\n <li>如果传入的偏移量参数为正,则仅保留切割之后的后一部分。</li>\\n <li>如果传入的参数为负,则相当于向前扩充缓冲区(只发生于析构函数中)。其源码如下。</li>\\n</ul>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">ScopedMemoryClip</span><span class=\\"o\\">::</span><span class=\\"n\\">clip</span><span class=\\"p\\">(</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"c1\\">// 获取到 gptr</span>\\n <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">gpos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">gCurrent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"c1\\">// 偏移缓冲区地址,并修改缓冲区大小</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// pptr 的位置减去缓冲区新地址,作为 pptr 的新位置</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">ppos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pCurrent</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 设置 gptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setg</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">gpos</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 设置 pptr 可达区域和位置</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">setp</span><span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_pBuffer</span> <span class=\\"o\\">+</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">pbump</span><span class=\\"p\\">(</span><span class=\\"n\\">ppos</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数小于偏移量,则可以将已写数据数设置为零</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">offset</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 如果已写数据数大于等于偏移量,则减去 offset</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">-=</span> <span class=\\"n\\">offset</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"c1\\">// 若已写字节数大于缓冲区容量,则设定为缓冲区容量</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_written</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_buffer</span><span class=\\"p\\">.</span><span class=\\"n\\">_bufferSize</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/gbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/streambuf/pbump/</li>\\n <li>http://www.cplusplus.com/reference/iostream/ios/init/</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T02:04:55+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 30820 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一amf-数据类型定义\\" id=\\"markdown-toc-一amf-数据类型定义\\">一、AMF 数据类型定义</a> <ul>\\n <li><a href=\\"#1数据类型\\" id=\\"markdown-toc-1数据类型\\">1、数据类型</a></li>\\n <li><a href=\\"#2undefined-type\\" id=\\"markdown-toc-2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</a></li>\\n <li><a href=\\"#3null-type\\" id=\\"markdown-toc-3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</a></li>\\n <li><a href=\\"#4false-type\\" id=\\"markdown-toc-4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</a></li>\\n <li><a href=\\"#5true-type\\" id=\\"markdown-toc-5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</a></li>\\n <li><a href=\\"#6integer-type\\" id=\\"markdown-toc-6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</a></li>\\n <li><a href=\\"#7double-type\\" id=\\"markdown-toc-7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</a></li>\\n <li><a href=\\"#8string-type\\" id=\\"markdown-toc-8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</a></li>\\n <li><a href=\\"#9xmldocument-type\\" id=\\"markdown-toc-9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</a></li>\\n <li><a href=\\"#10date-type\\" id=\\"markdown-toc-10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</a></li>\\n <li><a href=\\"#11array-type\\" id=\\"markdown-toc-11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</a></li>\\n <li><a href=\\"#12object-type\\" id=\\"markdown-toc-12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</a></li>\\n <li><a href=\\"#13xml-type\\" id=\\"markdown-toc-13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</a></li>\\n <li><a href=\\"#14bytearray-type\\" id=\\"markdown-toc-14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</a></li>\\n <li><a href=\\"#15amf3-的使用\\" id=\\"markdown-toc-15amf3-的使用\\">15、AMF3 的使用</a> <ul>\\n <li><a href=\\"#151netconnection-and-amf-3\\" id=\\"markdown-toc-151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</a></li>\\n <li><a href=\\"#152netconnection-in-actionscript-30\\" id=\\"markdown-toc-152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</a></li>\\n <li><a href=\\"#153bytearray-idatainput-and-idataoutput\\" id=\\"markdown-toc-153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二binaryreaderwriter\\" id=\\"markdown-toc-二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></a> <ul>\\n <li><a href=\\"#1amf3-数据格式基础\\" id=\\"markdown-toc-1amf3-数据格式基础\\">1、AMF3 数据格式基础</a></li>\\n <li><a href=\\"#2序列化\\" id=\\"markdown-toc-2序列化\\">2、序列化</a></li>\\n <li><a href=\\"#3反序列化\\" id=\\"markdown-toc-3反序列化\\">3、反序列化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三packetreaderwriter\\" id=\\"markdown-toc-三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></a> <ul>\\n <li><a href=\\"#1packetreader\\" id=\\"markdown-toc-1packetreader\\">1、PacketReader</a> <ul>\\n <li><a href=\\"#11封装-memoryinputstream\\" id=\\"markdown-toc-11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></a></li>\\n <li><a href=\\"#12收缩缓冲区\\" id=\\"markdown-toc-12收缩缓冲区\\">1.2、收缩缓冲区</a></li>\\n <li><a href=\\"#13构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-13构造函数拷贝构造函数和析构函数\\">1.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2packetwriter\\" id=\\"markdown-toc-2packetwriter\\">2、<code class=\\"language-plaintext highlighter-rouge\\">PacketWriter</code></a> <ul>\\n <li><a href=\\"#21封装memoryoutputstream\\" id=\\"markdown-toc-21封装memoryoutputstream\\">2.1、封装<code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code></a></li>\\n <li><a href=\\"#22封装-binarywriter\\" id=\\"markdown-toc-22封装-binarywriter\\">2.2、封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code></a></li>\\n <li><a href=\\"#23构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-23构造函数拷贝构造函数和析构函数\\">2.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四amfreader\\" id=\\"markdown-toc-四amfreader\\">四、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code></a> <ul>\\n <li><a href=\\"#1objectdef\\" id=\\"markdown-toc-1objectdef\\">1、<code class=\\"language-plaintext highlighter-rouge\\">ObjectDef</code></a></li>\\n <li><a href=\\"#2amfreader-定义\\" id=\\"markdown-toc-2amfreader-定义\\">2、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 定义</a> <ul>\\n <li><a href=\\"#21构造函数析构函数\\" id=\\"markdown-toc-21构造函数析构函数\\">2.1、构造函数、析构函数</a></li>\\n <li><a href=\\"#22简单封装-packetreader-的一些函数\\" id=\\"markdown-toc-22简单封装-packetreader-的一些函数\\">2.2、简单封装 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 的一些函数</a></li>\\n <li><a href=\\"#23设置-gptr-位置\\" id=\\"markdown-toc-23设置-gptr-位置\\">2.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 位置</a></li>\\n <li><a href=\\"#24判断类型\\" id=\\"markdown-toc-24判断类型\\">2.4、判断类型</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3解析-as3-null\\" id=\\"markdown-toc-3解析-as3-null\\">3、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Null</code></a></li>\\n <li><a href=\\"#4解析-as3-number\\" id=\\"markdown-toc-4解析-as3-number\\">4、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Number</code></a></li>\\n <li><a href=\\"#5解析-as3-integer\\" id=\\"markdown-toc-5解析-as3-integer\\">5、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code></a></li>\\n <li><a href=\\"#6解析-as3-boolean\\" id=\\"markdown-toc-6解析-as3-boolean\\">6、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code></a></li>\\n <li><a href=\\"#7开始引用与结束引用\\" id=\\"markdown-toc-7开始引用与结束引用\\">7、开始引用与结束引用</a></li>\\n <li><a href=\\"#8解析-as3-bytearray\\" id=\\"markdown-toc-8解析-as3-bytearray\\">8、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code></a></li>\\n <li><a href=\\"#9解析-as3-date\\" id=\\"markdown-toc-9解析-as3-date\\">9、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Date</code></a></li>\\n <li><a href=\\"#10解析-as3-dictionary\\" id=\\"markdown-toc-10解析-as3-dictionary\\">10、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Dictionary</code></a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。</p>\\n\\n<h3 id=\\"一amf-数据类型定义\\">一、AMF 数据类型定义</h3>\\n\\n<h4 id=\\"1数据类型\\">1、数据类型</h4>\\n\\n<p>各种数据类型的标示都在 AMF.h 中定义为宏</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cp\\">#define AMF_NUMBER 0x00 // 浮点数\\n#define AMF_BOOLEAN 0x01 // 布尔型\\n#define AMF_STRING 0x02 // 字符串\\n#define AMF_BEGIN_OBJECT 0x03 // 对象,开始\\n#define AMF_NULL 0x05 // null\\n#define AMF_UNDEFINED 0x06\\n#define AMF_REFERENCE 0x07\\n#define AMF_MIXED_ARRAY 0x08\\n#define AMF_END_OBJECT 0x09 // 对象,结束\\n#define AMF_BEGIN_TYPED_OBJECT 0x10\\n#define AMF_STRICT_ARRAY 0x0A\\n#define AMF_DATE 0x0B // 日期\\n#define AMF_LONG_STRING 0x0C // 字符串\\n#define AMF_UNSUPPORTED 0x0D\\n</span> \\n<span class=\\"cp\\">#define AMF_AVMPLUS_OBJECT 0x11\\n#define AMF_END 0xFF\\n</span> \\n<span class=\\"cp\\">#define AMF3_UNDEFINED 0x00\\n#define AMF3_NULL 0x01\\n#define AMF3_FALSE 0x02\\n#define AMF3_TRUE 0x03\\n#define AMF3_INTEGER 0x04\\n#define AMF3_NUMBER 0x05\\n#define AMF3_STRING 0x06\\n#define AMF3_DATE 0x08\\n#define AMF3_ARRAY 0x09\\n#define AMF3_OBJECT 0x0A\\n#define AMF3_BYTEARRAY 0x0C\\n#define AMF3_DICTIONARY 0x11\\n</span></code></pre></div></div>\\n\\n<p>并定义了一个枚举类表示数据类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">AMF</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"k\\">enum</span> <span class=\\"n\\">Type</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Null</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Boolean</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Integer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Number</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">String</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Date</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Array</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Object</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ByteArray</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Dictionary</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RawObjectContent</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">End</span>\\n <span class=\\"p\\">};</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型标记表示,用于编码布尔值 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</h4>\\n\\n<p>true 类型由 true 类型标记表示,用于编码布尔值 true。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</h4>\\n\\n<p>在 AMF 3 中,整数使用可变长度的无符号 29 位整数进行序列化。ActionScript 3.0 中的整数类型 - 有符号 <code class=\\"language-plaintext highlighter-rouge\\">int</code> 类型和无符号 <code class=\\"language-plaintext highlighter-rouge\\">uint</code> 类型 - 也使用 29 位在 AVM+中表示。如果无符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">uint</code>) 的值大于等于 229 或者如果有符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">int</code>) 的值大于等于 228,则它将被 AVM+ 表示为 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型,并使用 AMF 3 double 类型进行序列化。</p>\\n\\n<h4 id=\\"7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</h4>\\n\\n<p>AMF 3 的 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型与 AMF 0 的 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 类型编码方式相同。此类型用于编码 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 或值大于等于 228 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">int</code> 或值大于等于 229 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">uint</code>。编码值始终是网络字节顺序中的 8 字节 IEEE-754 双精度浮点值 (低内存中的符号位)。</p>\\n\\n<h4 id=\\"8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</h4>\\n\\n<p>ActionScript String 值使用 AMF 3 中的单个 string 类型表示 - AMF 0 中的 <code class=\\"language-plaintext highlighter-rouge\\">string</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">long string</code> 类型的概念不再使用。可以使用对隐式字符串引用表中的索引将字符串作为先前发生的字符串的引用发送。字符串使用 UTF-8 编码 - 但是头可以描述字符串文本或字符串引用。空字符串永远不会作为引用发送。</p>\\n\\n<h4 id=\\"9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了新的 XML 类型 (参见 3.13),但是旧版的 XMLDocument 类型在语言中被保留为 <code class=\\"language-plaintext highlighter-rouge\\">flash.xml.XMLDocument</code>。与 AMF 0 类似,<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 的结构需要扁平化为字符串表示以进行序列化。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。XMLDocuments 可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</h4>\\n\\n<p>在 AMF 3 中,ActionScript Date 简单地作为自 1970 年 1 月 1 日午夜 (UTC 时区) 以来的毫秒数进行序列化。不发送本地时区信息。可以使用对隐式对象引用表中的索引将日期作为先前发生的日期实例的引用发送。</p>\\n\\n<h4 id=\\"11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</h4>\\n\\n<p>ActionScript 数组的类型和在数组中的位置是基于它们的索引性质描述的。以下表格概述了这些术语的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">strict</code>:仅包含序数(数字)索引</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">dense</code>:序数索引从 0 开始,并且在连续索引之间不存在间隙(即,从 0 到数组长度的每一个索引都被定义了)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">sparse</code>:包含至少两个索引之间的一个间隙</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">associative</code>:包含至少一个非序数(字符串)索引(有时称为 ECMA 数组)</li>\\n</ul>\\n\\n<p>AMF 将数组分为两部分,密集部分和关联部分。关联部分的二进制表示由名称/值对(可能没有)终止的空字符串。密集部分的二进制表示由密集部分的大小(可能为零)以及有序的值列表(可能没有)组成。在 AMF 中写入的顺序是密集部分的大小,一个以空字符串终止的名称/值对列表,然后是大小的值。数组可以通过使用隐式对象引用表的索引作为先前发生的数组的引用来发送。</p>\\n\\n<h4 id=\\"12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</h4>\\n\\n<p>AMF 3 中有一种类型用于处理 ActionScript 对象和自定义用户类。使用术语 “traits” 来描述类的定义特征。除了 “anonymous” 对象和 “typed” 对象,ActionScript 3.0 还引入了两个进一步的 traits 来描述如何序列化对象,即 “dynamic” 和 “externalizable”。以下表格概述了这些术语和它们的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Anonymous</code>:实际的 ActionScript 对象类型的实例或没有注册别名的类的实例(在反序列化时将其视为对象)。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Typed</code>:具有注册别名的类的实例。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Dynamic</code>:具有动态特征声明的类定义的实例;可以在运行时动态地从实例中添加和删除公共变量成员。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Externalizable</code>:实现 flash.utils.IExternalizable 的类的实例,它完全控制其成员的序列化(特征信息中不包含属性名)。</li>\\n</ul>\\n\\n<p>在这些特征之外,对象的特征信息还可能包括在类上定义的一组公共变量和公共可读写属性名称(即不是函数的公共成员)。成员名称的顺序很重要,因为在特征信息之后的成员值将按照完全相同的顺序出现。这些成员被视为密封成员,因为它们是由类型明确定义的。</p>\\n\\n<p>如果类型是动态的,则在密封成员之后可以包括一个进一步的部分,该部分将动态成员列为名称/值对。当遇到空字符串名称时,继续读取动态成员。</p>\\n\\n<p>对象可以通过使用隐式对象引用表中的索引来作为先前发生对象的引用。此外,还可以通过使用隐式特征引用表中的索引将特征信息发送为先前发生的一组特征的引用。</p>\\n\\n<h4 id=\\"13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了一种新的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型,支持 E4X 语法。为了序列化,需要将 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型展平成字符串表示形式。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。<code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 XML 实例的引用发送。请注意,这种编码对 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 的使用造成了一些理论限制。每个 UTF-8 编码的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例的字节长度最大为 228-1 字节(大约 256 MB)。</p>\\n\\n<h4 id=\\"14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</h4>\\n\\n<p>用于保存字节数组,即 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>。AMF 3 使用可变长度编码 29 位整数序列化此类型,其中包括字节长度前缀,然后是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 的原始字节。<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"15amf3-的使用\\">15、AMF3 的使用</h4>\\n\\n<h5 id=\\"151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</h5>\\n\\n<p>除了序列化 ActionScript 类型外,AMF 还可用于远程服务的异步调用。可使用简单的消息结构将一批请求发送到远程端点。此消息结构的格式为 AMF 0(参见[AMF0])。可以使用特殊的 <code class=\\"language-plaintext highlighter-rouge\\">avmplus-object-marker</code> 类型将上下文头值或消息正文切换到 AMF 3 编码。</p>\\n\\n<h5 id=\\"152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</h5>\\n\\n<p>在 ActionScript 3.0 中,NetConnection 的限定类名是 flash.net.NetConnection。这个类仍然使用响应器来处理远程端点的结果和状态响应,但是现在需要强类型的 Responder 类。完全限定的类名是 flash.net.Responder。除了正常的结果和状态响应之外,NetConnection 还会分发事件,开发人员可以添加监听器。下面是这些事件的概述:</p>\\n\\n<ul>\\n <li>当异常异步抛出时触发,例如来自本机异步代码。</li>\\n <li>当输入或输出错误导致网络操作失败时触发。</li>\\n <li>当 NetConnection 对象报告其状态或错误条件时触发。</li>\\n <li>如果对 NetConnection.call() 的调用尝试连接到调用者安全沙箱外的服务器,则会触发。</li>\\n</ul>\\n\\n<h5 id=\\"153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></h5>\\n\\n<p>ActionScript 3.0 引入了一种新类型,用于支持以字节数组形式处理原始数据,即 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.ByteArray</code>。为了协助 ActionScript 对象序列化和复制,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实现了 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataInput</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataOutput</code>。这些接口指定了帮助将常见类型写入字节流的实用方法。两个感兴趣的方法是 <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput.writeObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">IDataInput.readObject</code>。这些方法使用 AMF 编码对象。使用的 AMF 版本由 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray.objectEncoding</code> 方法控制,该方法可以设置为 AMF 3 或 AMF 0。枚举类型 <code class=\\"language-plaintext highlighter-rouge\\">flash.net.ObjectEncoding</code> 包含 AMF 版本的常量:分别为 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF0</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF3</code>。</p>\\n\\n<p>请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray.writeObject</code> 使用一个版本的 AMF 对整个对象进行编码。与 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 不同,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 不会从 AMF 0 开始,然后将 <code class=\\"language-plaintext highlighter-rouge\\">objectEncoding</code> 属性设置为 AMF 3 并切换到 AMF 3。还请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 为每个 <code class=\\"language-plaintext highlighter-rouge\\">readObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">writeObject</code> 调用使用新的对象、对象特征和字符串的隐式引用表。</p>\\n\\n<h3 id=\\"二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></h3>\\n\\n<h4 id=\\"1amf3-数据格式基础\\">1、AMF3 数据格式基础</h4>\\n\\n<p>首先介绍一下变长整数(Variable Length Integer),比如 UInt32 如下。</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档,这是一种压缩方式的整数存储,且每一字节都对后面的数据具有预知作用。那么字符串如何处理呢?下面是字符串的处理方式,AMF0 和 AMF3 都才用 UTF-8 编码方式,并做如下压缩处理:</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档。</p>\\n\\n<h4 id=\\"2序列化\\">2、序列化</h4>\\n\\n<p>序列化包括 8 位、16 位、32 位,以及 UTF-8 和 UTF-16(I guess)编码的 String,还有原生数据(Raw Data)、变长无符号整数(Variable Length Unsigned Integer)以及 IP 地址。所谓序列化就是按照指定格式编写各种对象、基础数据类型值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryWriter</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Net</span><span class=\\"o\\">::</span><span class=\\"n\\">SocketAddress</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryWriter</span> <span class=\\"n\\">BinaryWriterNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>请注意其中名为 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriterNull</code> 的成员。构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">writeRaw</code> 是简单地封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter::writeRaw()</code>,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入整数实现如下,用的是从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的重载运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span> \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入变长整数,这段代码含义也一目了然,就是读取变长无符号 32 位整数、64 位整数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">21</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span> <span class=\\"c1\\">// 4 bytes maximum</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">22</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">63</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Can give 10 bytes!</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">shift</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入 IP 地址的两个函数暂略。</p>\\n\\n<h4 id=\\"3反序列化\\">3、反序列化</h4>\\n\\n<p>反序列化就是从指定格式的数据中读出各类型的数据值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryReader</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitEncoded</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString8</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString16</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">read16</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read32</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryReader</span> <span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造与析构函数都很简单:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">)</span> <span class=\\"o\\">:</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取原生数据(Raw Data):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写整数,用的是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter</code> 的重载运算符:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读写整数依旧使用从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt8</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt16</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read16</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read32</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取变长整数,分别针对 <code class=\\"language-plaintext highlighter-rouge\\">UInt32</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">UInt64</code>,要理解 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 的变长整数才能理解这个:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt64</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt64</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></h3>\\n\\n<h4 id=\\"1packetreader\\">1、PacketReader</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define PACKETRECV_SIZE 2048\\nclass PacketReader: public BinaryReader {\\npublic:\\n PacketReader(const Poco::UInt8* buffer,Poco::UInt32 size);\\n PacketReader(PacketReader&amp;);\\n virtual ~PacketReader();\\n const Poco::UInt32 fragments;\\n Poco::UInt32 available(); // 可读字节数\\n Poco::UInt8* current();\\n Poco::UInt32 position(); // 获取当前的相对位置(相对于起始位置的)\\n void reset(Poco::UInt32 newPos = 0); // 设定当前位置\\n void shrink(Poco::UInt32 rest);\\n void next(Poco::UInt32 size);\\nprivate:\\n MemoryInputStream _memory;\\n};\\n</code></pre></div></div>\\n\\n<h6 id=\\"11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">current</code>:当前绝对位置(内存地址)</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code>:当前位置(绝对位置)减去缓冲区起始位置</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"12收缩缓冲区\\">1.2、收缩缓冲区</h6>\\n\\n<p>封装了 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">resize</code>。不过由于前面的 <code class=\\"language-plaintext highlighter-rouge\\">if</code> 语句影响,传给 resize 的参数一定不会大于缓冲区的当前大小。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">shrink</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">rest</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">rest</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">available</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"rest %u more upper than available %u bytes\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">rest</span><span class=\\"p\\">,</span><span class=\\"n\\">available</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">rest</span> <span class=\\"o\\">=</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"o\\">+</span> <span class=\\"n\\">rest</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"13构造函数拷贝构造函数和析构函数\\">1.3、构造函数、拷贝构造函数和析构函数</h6>\\n\\n<p>构造函数先调用父类 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReader</code> 的构造函数,并初始化 <code class=\\"language-plaintext highlighter-rouge\\">fragments</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">_memory</code> 输入流的缓冲区。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">fragments</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"c1\\">// Consctruction by copy</span>\\n<span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">fragments</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">fragments</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">PacketReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">PacketReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2packetwriter\\">2、<code class=\\"language-plaintext highlighter-rouge\\">PacketWriter</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">PacketWriter</span><span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">BinaryWriter</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">good</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">clear</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">limit</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"nl\\">private:</span>\\n <span class=\\"n\\">MemoryOutputStream</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">*</span> <span class=\\"n\\">_pOther</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"21封装memoryoutputstream\\">2.1、封装<code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">good</code>:不过 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code> 也是封装的 <code class=\\"language-plaintext highlighter-rouge\\">std::ostream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">good</code> 函数,True if no error flags are set.</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">bool</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">good</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">good</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">written</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">length</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code>:设置缓冲区的指针位置,即 <code class=\\"language-plaintext highlighter-rouge\\">position</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">newPos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">newPos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">next</code>:移动缓冲区指针</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin</code>:返回缓冲区的起始地址</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">begin</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">clear</code>:其实就是修改 written 和 position,使得指定位置后面的数据在以后写的时候可以被覆盖,并不是真正的清除。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">clear</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">pos</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">pos</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">limit</code>:封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">resize</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">limit</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">length</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">length</span> <span class=\\"o\\">==</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">length</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">length</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Limit '%d' more upper than buffer size '%d' bytes\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">length</span><span class=\\"p\\">,</span><span class=\\"n\\">_size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">length</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_size</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">resize</span><span class=\\"p\\">(</span><span class=\\"n\\">length</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"22封装-binarywriter\\">2.2、封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">flush</code>:封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">flush</code>,不过 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">flush</code> 实际上是从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter</code> 继承而来的。其作用是「Flushes the underlying stream」。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">flush</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_pOther</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_pOther</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">_pOther</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">written</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h6 id=\\"23构造函数拷贝构造函数和析构函数\\">2.3、构造函数、拷贝构造函数和析构函数</h6>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">buffer</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pOther</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_size</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"c1\\">// Consctruction by copy</span>\\n<span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">other</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">_pOther</span><span class=\\"p\\">(</span><span class=\\"o\\">&amp;</span><span class=\\"n\\">other</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_memory</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">_memory</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_size</span><span class=\\"p\\">(</span><span class=\\"n\\">other</span><span class=\\"p\\">.</span><span class=\\"n\\">_size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>注意析构函数中会进行 <code class=\\"language-plaintext highlighter-rouge\\">flush</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">PacketWriter</span><span class=\\"o\\">::~</span><span class=\\"n\\">PacketWriter</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"四amfreader\\">四、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code></h3>\\n\\n<h4 id=\\"1objectdef\\">1、<code class=\\"language-plaintext highlighter-rouge\\">ObjectDef</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ObjectDef</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span> \\n <span class=\\"n\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">(</span><span class=\\"n\\">amf3</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">dynamic</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">externalizable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">count</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">arrayType</span><span class=\\"p\\">(</span><span class=\\"n\\">arrayType</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">string</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">hardProperties</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reset</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">dynamic</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">externalizable</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">count</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2amfreader-定义\\">2、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 定义</h4>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 作为其成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">AMFReader</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">AMFReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">);</span>\\n <span class=\\"o\\">~</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readSimpleObject</span><span class=\\"p\\">(</span><span class=\\"n\\">AMFSimpleObject</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">object</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">read</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">readNumber</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Int32</span> <span class=\\"n\\">readInteger</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readBoolean</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">readByteArray</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Timestamp</span> <span class=\\"n\\">readDate</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readObject</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readArray</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readDictionary</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">weakKeys</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readKey</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">readItem</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">readRawObjectContent</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readNull</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"kt\\">void</span> <span class=\\"n\\">startReferencing</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">stopReferencing</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">;</span>\\n \\n<span class=\\"nl\\">private:</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*&gt;</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_stringReferences</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_classDefReferences</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">_amf0References</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_amf0Reset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_reset</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">_amf3</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">_referencing</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"21构造函数析构函数\\">2.1、构造函数、析构函数</h5>\\n\\n<p>参数为 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code>,会初始化一些成员变量。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">(</span><span class=\\"n\\">PacketReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">reader</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">reader</span><span class=\\"p\\">(</span><span class=\\"n\\">reader</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_amf3</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_amf0Reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_referencing</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>析构时,会逐一释放 <code class=\\"language-plaintext highlighter-rouge\\">_objectDefs</code> 中对象的内存:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMFReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">AMFReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">list</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*&gt;::</span><span class=\\"n\\">iterator</span> <span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span><span class=\\"o\\">!=</span><span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">delete</span> <span class=\\"o\\">*</span><span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"22简单封装-packetreader-的一些函数\\">2.2、简单封装 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 的一些函数</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code>:操作指针位置</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_reset</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code>:根据当前缓冲区大小和 <code class=\\"language-plaintext highlighter-rouge\\">written</code> 计算得到</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">current</code>:<code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 内存地址</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">*</span><span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"23设置-gptr-位置\\">2.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 位置</h5>\\n\\n<p>其实 <code class=\\"language-plaintext highlighter-rouge\\">pptr</code> 也被影响了,但是在 <code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 中只用 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code>。调用构造函数的时候,<code class=\\"language-plaintext highlighter-rouge\\">reset</code> 被设为 0,其后在每次读取数据的时候都会影响 <code class=\\"language-plaintext highlighter-rouge\\">reset</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">reset</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_reset</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_reset</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"24判断类型\\">2.4、判断类型</h5>\\n\\n<p>分析请看注释:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">followingType</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先 <code class=\\"language-plaintext highlighter-rouge\\">reset</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">back</span><span class=\\"p\\">()</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">amf3</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>是 AMF0 类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果没有可读数据了,则返回 AMF::End。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">available</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">End</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>开始读了,先读到的表示 AMF 数据类型。要注意的是调用 current 并不改变指针的位置,所以你会在线面看到调用 next。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt8</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_amf3</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF_AVMPLUS_OBJECT</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_amf3</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">available</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">End</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>AMF3 类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">switch</span><span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>Undefined 和 null 都当做 null。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_UNDEFINED</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_NULL</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>false 和 true 都是 boolean。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_FALSE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_TRUE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_INTEGER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Integer</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_NUMBER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">String</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_DATE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Array</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_DICTIONARY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Dictionary</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Object</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF3_BYTEARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">ByteArray</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>落到 default 手里的话,就跳过这个字节,读取下一个。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"nl\\">default:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unknown AMF3 type %.2x\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>AMF0 类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">switch</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">null</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">null</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_UNDEFINED</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_NULL</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BOOLEAN</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_NUMBER</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">long string</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">string</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">string</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_LONG_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_STRING</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">String</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">mixed array</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">strict array</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">array</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_MIXED_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_STRICT_ARRAY</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Array</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_DATE</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">begin object</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">begin typed object</code> 都是 <code class=\\"language-plaintext highlighter-rouge\\">object</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BEGIN_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_BEGIN_TYPED_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Object</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是引用,就跳过表示类型值的这个字节。这个先留下来,带我们分析完 readArray 和 readObject 再回头看。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_REFERENCE</span><span class=\\"p\\">:</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read16</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">reference</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_amf0References</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF0 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_amf0Reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_amf0References</span><span class=\\"p\\">[</span><span class=\\"n\\">reference</span><span class=\\"p\\">]);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果没了,或者不支持,或者都不是,就跳过这个字节,递归继续读取:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_END_OBJECT</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF end object type without begin object type before\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">case</span> <span class=\\"n\\">AMF_UNSUPPORTED</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unsupported type in AMF format\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">default</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Unknown AMF type %.2x\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">followingType</code> 是这个类的核心,每个具体的数据类型的分析都依赖于它的判断。这些类型的解析,会在下一篇文章中介绍。</p>\\n\\n<h4 id=\\"3解析-as3-null\\">3、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Null</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readNull</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先 reset 一下是惯例,就像糗百上的割一样。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span> \\n</code></pre></div></div>\\n\\n<p>AMF 数据类型</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">Null</code>,跳过该字节,并返回</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>判断错误</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Null type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4解析-as3-number\\">4、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Number</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">double</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readNumber</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Null</code> 会被悲催的跳过:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 呀 :(</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Number type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过该字节吧</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>返回吧,返回之前还用到 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 的运算符,注意 AS3 中的 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 就是 C++ 的 <code class=\\"language-plaintext highlighter-rouge\\">double</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5解析-as3-integer\\">5、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Int32</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readInteger</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code> 类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>Null 的话:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code> 或者 Number 的话。。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Integer</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Integer type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过吧。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>终于是 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Number</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Int32</span><span class=\\"p\\">)</span><span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读一个变长的 32 位无符号整数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Forced in AMF3 here!</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果大于 3.5 个字节所能表示的最大无符号整数值(<code class=\\"language-plaintext highlighter-rouge\\">268435455</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">0xFFFFFFF</code>),则减去 <code class=\\"language-plaintext highlighter-rouge\\">0x2FFFFFFF</code>(这还不是太理解,有能解释的朋友给留个言,或者发 email 给我 )</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">268435455</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">value</span> <span class=\\"o\\">-=</span> <span class=\\"p\\">(</span><span class=\\"mi\\">1</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"mi\\">29</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"6解析-as3-boolean\\">6、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readBoolean</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">Null</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>居然不是 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code> 的话。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Boolean</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Boolean type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果是 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 的话,返回 <code class=\\"language-plaintext highlighter-rouge\\">true</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">false</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span><span class=\\"o\\">==</span> <span class=\\"n\\">AMF3_FALSE</span> <span class=\\"o\\">?</span> <span class=\\"nb\\">false</span> <span class=\\"o\\">:</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>不是 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">AMF0</code> 喽:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span><span class=\\"o\\">==</span><span class=\\"mh\\">0x00</span> <span class=\\"o\\">?</span> <span class=\\"nb\\">false</span> <span class=\\"o\\">:</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"7开始引用与结束引用\\">7、开始引用与结束引用</h4>\\n\\n<p>如下这两个函数会在 <code class=\\"language-plaintext highlighter-rouge\\">FlowConnection</code> 中调用。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">startReferencing</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_referencing</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">stopReferencing</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_referencing</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"8解析-as3-bytearray\\">8、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code></h4>\\n\\n<p>先回顾一下 AMF3 中的ByteArray 的数据格式:</p>\\n\\n<p>注意到,首先要读取一个变长无符号 32 位整数,但是最低位是 1,只有 28 位用于表示数据长度。解释完这里,下面的解析过程才好理解。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readByteArray</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Null</code> 就返回 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReaderNull</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>,也返回 <code class=\\"language-plaintext highlighter-rouge\\">BinaryReaderNull</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">ByteArray</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF ByteArray type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过这个字节:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>注意 position 返回的是相对位置,与 AS3 中一样。<code class=\\"language-plaintext highlighter-rouge\\">reference</code> 表示这个地址(简单说,引用就是地址嘛)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>读取一个变长 32 位无符号整数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>最低位是 1 的话,<code class=\\"language-plaintext highlighter-rouge\\">isInline</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,否则为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>右移一位,因为那一位是标志位,上面解释过了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">isInline</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">true</code>,表示是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">_referencing</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">true</code> 的话(这是一个 <code class=\\"language-plaintext highlighter-rouge\\">vector</code>),push back this reference:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>不符合 ByteArray 的格式定义的话:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">size</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>移动到这个 reference 的位置,<code class=\\"language-plaintext highlighter-rouge\\">_references[size]</code> 就是这个位置(相对)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">size</span><span class=\\"p\\">]);</span> <span class=\\"c1\\">// TODO size 作为索引,还没有完全理解</span>\\n</code></pre></div></div>\\n\\n<p>读取这个 reference 的 size 值给 size对象(注意 size 是这个函数传入的引用参数,其值可以被修改)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>把读取完 ByteArraty 的 PacketReader 返回:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"n\\">reader</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>最后强调一点,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 的数据段最大长度为 228 -1 字节,约为 256 MB。</p>\\n\\n<h4 id=\\"9解析-as3-date\\">9、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Date</code></h4>\\n\\n<p>先看下 <code class=\\"language-plaintext highlighter-rouge\\">Date</code> 的数据格式:</p>\\n\\n<p>下面开始分析:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Timestamp</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readDate</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>惯例:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>Null 的话,就返回当前时间:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是 Date 类型,也返回当前时间:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Date</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Date type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">double</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是 AMF3:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先读取 flag,最低一位必须是 1,其他位丢到垃圾桶。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">flags</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>当前相对位置。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>是 1 就 push back 到 <code class=\\"language-plaintext highlighter-rouge\\">_references</code> 里。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">flags</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>读取一个 double,到 result 里(result 也是 double 类型哦~)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位不是 1,麻烦不少哒。。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flags</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果 flag 超了,就返回当前时间作为时间戳作为 Date。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">flags</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">Timestamp</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>这段与 ByteArray 那段一样:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">flags</span><span class=\\"p\\">]);</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>返回喽~</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"nf\\">Timestamp</span><span class=\\"p\\">((</span><span class=\\"n\\">Timestamp</span><span class=\\"o\\">::</span><span class=\\"n\\">TimeVal</span><span class=\\"p\\">)</span> <span class=\\"n\\">result</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"n\\">reader</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>读俩,因为是 double(64 位):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">2</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Timezone, useless</span>\\n</code></pre></div></div>\\n\\n<p>返回喽~</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"nf\\">Timestamp</span><span class=\\"p\\">((</span><span class=\\"n\\">Timestamp</span><span class=\\"o\\">::</span><span class=\\"n\\">TimeVal</span><span class=\\"p\\">)</span> <span class=\\"n\\">result</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"10解析-as3-dictionary\\">10、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Dictionary</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">AMFReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readDictionary</span><span class=\\"p\\">(</span><span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">weakKeys</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>下面这段咱就略了。。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">reset</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Type</span> <span class=\\"n\\">type</span> <span class=\\"o\\">=</span> <span class=\\"n\\">followingType</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">==</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Null</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">type</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">AMF</span><span class=\\"o\\">::</span><span class=\\"n\\">Dictionary</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Type %.2x is not a AMF Dictionary type\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">type</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>跳过 type:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// AMF3</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">next</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// marker</span>\\n</code></pre></div></div>\\n\\n<p>当前相对位置值作为 <code class=\\"language-plaintext highlighter-rouge\\">reference</code>,再读个 <code class=\\"language-plaintext highlighter-rouge\\">size</code>,还是最低位必须为 1,不是就返回 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">UInt32</span> <span class=\\"n\\">reference</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">isInline</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">size</span> <span class=\\"o\\">&gt;&gt;=</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">isInline</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">size</span><span class=\\"o\\">&gt;</span><span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"AMF3 reference not found\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>下面要调用到 <code class=\\"language-plaintext highlighter-rouge\\">ObjectRef</code> 构造函数,这里再把其实现拿出来看看,其实主要是初始化了哪些成员。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">arrayType</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">:</span> <span class=\\"n\\">amf3</span><span class=\\"p\\">(</span><span class=\\"n\\">amf3</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">dynamic</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">externalizable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">count</span><span class=\\"p\\">(</span><span class=\\"mi\\">0</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">arrayType</span><span class=\\"p\\">(</span><span class=\\"n\\">arrayType</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>可以看到要有一个 amf3,还有 <code class=\\"language-plaintext highlighter-rouge\\">reset</code> 置为 0,<code class=\\"language-plaintext highlighter-rouge\\">dynamic</code> 置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,<code class=\\"language-plaintext highlighter-rouge\\">externalizable</code> 也是 <code class=\\"language-plaintext highlighter-rouge\\">false</code>,<code class=\\"language-plaintext highlighter-rouge\\">count</code> 是 0,<code class=\\"language-plaintext highlighter-rouge\\">arrayType</code> 成员要赋值。</p>\\n\\n<p>上面是插播哦,下面还要继续哒。创建这么一个对象,注意是 new 出来的,所以我们在《OpenRTMFP/Cumulus Primer(16)AMF解析之AMFReader》一文中提到了 AMFReader 的析构函数中要对 <code class=\\"language-plaintext highlighter-rouge\\">_objectRef</code> 的每个元素逐一析构的。<code class=\\"language-plaintext highlighter-rouge\\">arrayType</code> 就设置为 <code class=\\"language-plaintext highlighter-rouge\\">AMF3_DICTIONARY</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ObjectDef</span><span class=\\"o\\">*</span> <span class=\\"n\\">pObjectDef</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nf\\">ObjectDef</span><span class=\\"p\\">(</span><span class=\\"n\\">_amf3</span><span class=\\"p\\">,</span> <span class=\\"n\\">AMF3_DICTIONARY</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">dynamic</span><span class=\\"o\\">=</span><span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_objectDefs</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">pObjectDef</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位是 1,就直接 push back,跟之前一样。不过这里多了一个 <code class=\\"language-plaintext highlighter-rouge\\">pObjectDef</code>,所以还要设置一下它的计数为 <code class=\\"language-plaintext highlighter-rouge\\">size</code>,就是 <code class=\\"language-plaintext highlighter-rouge\\">dictionary</code> 数据大小。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isInline</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_referencing</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_references</span><span class=\\"p\\">.</span><span class=\\"n\\">push_back</span><span class=\\"p\\">(</span><span class=\\"n\\">reference</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">=</span> <span class=\\"n\\">size</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果标志位是 0,就把 <code class=\\"language-plaintext highlighter-rouge\\">count</code> 设置为下一个变长整数值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">reset</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">position</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">reset</span><span class=\\"p\\">(</span><span class=\\"n\\">_references</span><span class=\\"p\\">[</span><span class=\\"n\\">size</span><span class=\\"p\\">]);</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"mi\\">1</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">pObjectDef</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">count</span> <span class=\\"o\\">*=</span> <span class=\\"mi\\">2</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>读一个字节,如果最小位是 1,weakKeys 就是 true,否则为 false。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">weakKeys</span> <span class=\\"o\\">=</span> <span class=\\"n\\">reader</span><span class=\\"p\\">.</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span> <span class=\\"o\\">&amp;</span> <span class=\\"mh\\">0x01</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">return</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</title>\\n \\t<meta name=\\"description\\" content=\\"CumulusServer 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 CumulusEdge 和 CumulusServer 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 CumulusServer 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-15T14:26:58+00:00\\" class=\\"by-line\\">15 Apr 2012, 广州 | 作者 麦克船长 | 总计 3844 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1绑定地址\\" id=\\"markdown-toc-1绑定地址\\">1、绑定地址</a></li>\\n <li><a href=\\"#2cumulusserver-接收数据\\" id=\\"markdown-toc-2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</a></li>\\n <li><a href=\\"#3如果-cumulusedge-端口存在且-edge-socket-可用\\" id=\\"markdown-toc-3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</a></li>\\n <li><a href=\\"#4cumulusserver-和-cumulusedge-的-socket-都没有数据\\" id=\\"markdown-toc-4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</a></li>\\n <li><a href=\\"#5发送方的-ip-被禁\\" id=\\"markdown-toc-5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</a></li>\\n <li><a href=\\"#6数据包长度小于可能的最小值12\\" id=\\"markdown-toc-6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</a></li>\\n</ul>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。</p>\\n\\n<p>本所要介绍的这个主循环在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(const volatile bool&amp; terminate)</code> 函数中。RTMFPServer覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"1绑定地址\\">1、绑定地址</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">address</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">绑定</span><span class=\\"n\\">CumulusEdge</span><span class=\\"err\\">的</span> <span class=\\"n\\">IP</span> <span class=\\"err\\">地址和端口:</span>\\n\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">edgesAddress</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>发送者(Client)的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"n\\">sender</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">buff</span><span class=\\"p\\">[</span><span class=\\"n\\">PACKETRECV_SIZE</span><span class=\\"p\\">];</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">stop</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">idle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">realTime</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 读取:把数据存到 <code class=\\"language-plaintext highlighter-rouge\\">buff</code>,把发送者地址赋给 <code class=\\"language-plaintext highlighter-rouge\\">sender</code>,把所读长度返回给 <code class=\\"language-plaintext highlighter-rouge\\">size</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>处理 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span> <span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span> <span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span> <span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">Edge</span><span class=\\"o\\">*</span> <span class=\\"n\\">pEdge</span> <span class=\\"o\\">=</span> <span class=\\"n\\">edges</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pEdge</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pEdge</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 空闲:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">idle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>主线程等待一秒。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">isElapsed</span><span class=\\"p\\">(</span><span class=\\"n\\">_freqManage</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>Just middle session</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Sessions</span><span class=\\"o\\">::</span><span class=\\"n\\">Iterator</span> <span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Middle</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMiddle</span> <span class=\\"o\\">=</span> <span class=\\"k\\">dynamic_cast</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Middle</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMiddle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pMiddle</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isBanned</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">INFO</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Data rejected because client %s is banned\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">().</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">size</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">RTMFP_MIN_PACKET_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Invalid packet\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">PacketReader</span> <span class=\\"nf\\">packet</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Session</span><span class=\\"o\\">*</span> <span class=\\"n\\">pSession</span> <span class=\\"o\\">=</span> <span class=\\"n\\">findSession</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFP</span><span class=\\"o\\">::</span><span class=\\"n\\">Unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">));</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">checked</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">commitCookie</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pSession</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>给 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 或者自己(<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>)的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">setEndPoint</span><span class=\\"p\\">(</span><span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">?</span> <span class=\\"n\\">_edgesSocket</span> <span class=\\"o\\">:</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">,</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">delete</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的第二篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-14T11:20:46+00:00\\" class=\\"by-line\\">14 Apr 2012, 广州 | 作者 麦克船长 | 总计 18890 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一cumulus-启动源码分析\\" id=\\"markdown-toc-一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数\\" id=\\"markdown-toc-1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</a></li>\\n <li><a href=\\"#2maincpp-中的-cumulusserver-构造函数\\" id=\\"markdown-toc-2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</a></li>\\n <li><a href=\\"#3maincpp-中的-cumulusserver-的-initialize-成员函数\\" id=\\"markdown-toc-3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</a></li>\\n <li><a href=\\"#4maincpp-中的-cumulusserver-的-main-成员函数\\" id=\\"markdown-toc-4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</a></li>\\n <li><a href=\\"#5cumulusserver-是-serverapplication-的子类\\" id=\\"markdown-toc-5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</a></li>\\n <li><a href=\\"#6serverapplication-是-application-的子类\\" id=\\"markdown-toc-6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</a></li>\\n <li><a href=\\"#7反初始化\\" id=\\"markdown-toc-7反初始化\\">7、反初始化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二cumulusserver-各项交互功能的源码解读\\" id=\\"markdown-toc-二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</a> <ul>\\n <li><a href=\\"#1命令行选项设定\\" id=\\"markdown-toc-1命令行选项设定\\">1、命令行选项设定</a></li>\\n <li><a href=\\"#2处理命令行选项\\" id=\\"markdown-toc-2处理命令行选项\\">2、处理命令行选项</a></li>\\n <li><a href=\\"#6dump-logs\\" id=\\"markdown-toc-6dump-logs\\">6、Dump logs</a></li>\\n <li><a href=\\"#3停止运行\\" id=\\"markdown-toc-3停止运行\\">3、停止运行</a></li>\\n <li><a href=\\"#4载入配置\\" id=\\"markdown-toc-4载入配置\\">4、载入配置</a></li>\\n <li><a href=\\"#5处理日志\\" id=\\"markdown-toc-5处理日志\\">5、处理日志</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三maincpp-的-main-函数源码分析\\" id=\\"markdown-toc-三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数中的-server\\" id=\\"markdown-toc-1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></a></li>\\n <li><a href=\\"#2maincpp-中-main-函数的-serverstart\\" id=\\"markdown-toc-2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></a></li>\\n <li><a href=\\"#3回顾一下整个启动流程\\" id=\\"markdown-toc-3回顾一下整个启动流程\\">3、回顾一下整个启动流程</a></li>\\n <li><a href=\\"#4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\" id=\\"markdown-toc-4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。首先要知道的是,OpenRTMFP/Cumulus 中使用到的库有 Poco、OpenSSL 和 Lua。</p>\\n\\n<h3 id=\\"一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</h4>\\n\\n<p>入口在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"kt\\">int</span> <span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">argv</span><span class=\\"p\\">[])</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先检查内存泄露,不过目前这个开发中的项目还没有实现这个功能,只是个空函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">DetectMemoryLeak</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后会创建一个匿名的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象,并调用其 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,该函数由 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 从 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 中继承而来,而 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 由是从 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 继承而来的。<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象调用 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,实际是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是调用 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的函数,而该 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。如果 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数被调用时抛出异常,则不会执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,但仍然会执行 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Runs the application by performing additional initializations</span>\\n <span class=\\"c1\\">// and calling the main() method.</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">CumulusServer</span><span class=\\"p\\">().</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"n\\">argv</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">()</span><span class=\\"o\\">:</span> <span class=\\"n\\">_helpRequested</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span> <span class=\\"c1\\">// 显示帮助信息</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_middle</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_isInteractive</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pLogFile</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</h4>\\n\\n<p>在执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数之前,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 会启动 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,传入的参数就是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 自己,可以猜到 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run</code> 方法中,调用该函数时的参数是 <code class=\\"language-plaintext highlighter-rouge\\">this</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">Application</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">self</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>调用父函数 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的初始化函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">self</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>再继续下面的源码分析之前,先要知道,根据 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> ,<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 类有一些内置的配置属性,如下:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.path</code>: 可执行文件的绝对路径;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.name</code>: 可执行文件的文件名(含扩展名);</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.baseName</code>: 可执行文件的文件名(不含扩展名)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.dir</code>: 可执行文件的所在目录;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.configDir</code>: 配置文件所在目录;</li>\\n</ul>\\n\\n<p>所以下面就读取了可执行文件的所在目录,其中第二个参数表示默认值(即当前目录):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>然后读取配置文件,目录为上一句所得到的 <code class=\\"language-plaintext highlighter-rouge\\">dir</code>,文件名(不含扩展名)为 <code class=\\"language-plaintext highlighter-rouge\\">application.basename</code> 内置配置属性值,其默认值为 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>,然后加上点和扩展名 <code class=\\"language-plaintext highlighter-rouge\\">.ini</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">dir</span>\\n <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.baseName\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"CumulusServer\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">+</span> <span class=\\"s\\">\\".ini\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这样就加载完配置了。然后查看当前的进程是从命令行运行的(命令行是交互的,所以是 interactive),还是以 daemon 方式运行的,这个函数是ServerApplication的一个成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_isInteractive</span> <span class=\\"o\\">=</span> <span class=\\"n\\">isInteractive</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后获取表示日志文件所在目录的字符串,其中 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code> 是外置配置属性(配置文件中),其默认值为上面获取到的可执行文件路径(一般为当前路径)与 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 的组合,即一般为当前目录下的 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"nf\\">logDir</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.directory\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"logs\\"</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>创建日志文件目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">logDir</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n\\n</code></pre></div></div>\\n\\n<p>日志文件绝对路径,<code class=\\"language-plaintext highlighter-rouge\\">logs</code> 为默认的日志文件名(不含扩展名的部分),扩展名用0:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logDir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span> <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.name\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\".\\"</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pLogFile</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nf\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"0\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>用日志流打开日志文件(方式为追加写入):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Logs</code> 是一个方法类(其中的 <code class=\\"language-plaintext highlighter-rouge\\">public</code> 函数都是静态的),<code class=\\"language-plaintext highlighter-rouge\\">SetLogger</code> 的作用就是将Logs中的似有静态成员设置为某个 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code> 对象(由于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLogger</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</h4>\\n\\n<p>OpenRTMFP/Cumulus 是一个基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的服务端应用(准确的说是基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::ServerApplication</code> 的服务端应用)。如果没有特殊的启动要求,可以调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco/Application.h</code> 中定义的宏 <code class=\\"language-plaintext highlighter-rouge\\">POCO_APP_MAIN</code> 来完成初始化、日志和启动(该宏会根据不同的平台启用不同的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数)。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数在调用完 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数后,会调用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,该 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的定义在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&gt;&amp;</span> <span class=\\"n\\">args</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>首先看是否是要求帮助信息,<code class=\\"language-plaintext highlighter-rouge\\">displayHelp</code> 是借助 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::HelpFormatter</code> 实现的,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数会在调用时将 <code class=\\"language-plaintext highlighter-rouge\\">_helpRequested</code> 设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_helpRequested</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">displayHelp</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是,则进入启动状态,首先创建一个 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServerParams</code> 对象 <code class=\\"language-plaintext highlighter-rouge\\">params</code>,用来存储 OpenRTMFP/CumulusServer 的基本配置信息。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">RTMFPServerParams</span> <span class=\\"n\\">params</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">params</code> 存储 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的端口号和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的端口号:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"port\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RTMFP_DEFAULT_PORT</span><span class=\\"o\\">+</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getBool</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.activated\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port must have a positive value if \\\\\\n edges.activated is true. Server edges is \\\\\\n disactivated.\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">=</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">_pCirrus</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">SocketAddress</code> 的成员,是封装IP地址和端口号的对象。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_middle</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>UDB 所使用的缓冲区大小:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"udpBufferSize\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"keepAliveServer\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"keepAlivePeer\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>失败前 CumulusEdge 的尝试次数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"edges.attemptsBeforeFallback\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> 函数是 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中必须调用的,意为等待终止运行的操作请求。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// wait for CTRL-C or kill</span>\\n <span class=\\"n\\">waitForTerminationRequest</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>一旦接收到终止操作的请求,就会执行下面这句,用以退出 OpenRTMFP/Cumulus 的运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Stop the server</span>\\n <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">stop</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">catch</code> 一些可能产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Configuration problem : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>OpenRTMFP/CumulusServer 停止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"n\\">Application</span><span class=\\"o\\">::</span><span class=\\"n\\">EXIT_OK</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 对其子类有如下要求:</p>\\n\\n<ul>\\n <li>Subsystems must be registered in the constructor.</li>\\n <li>All non-trivial initializations must be made in the <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>` method.</li>\\n <li>At the end of the <code class=\\"language-plaintext highlighter-rouge\\">main()</code> method, <code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> should be called.</li>\\n</ul>\\n\\n<h4 id=\\"6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</h4>\\n\\n<p>Application 对其子类的要求是,如下这些成员函数必须被覆盖:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> (the one-argument, protected variant):上一篇已介绍过。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code>:下面会介绍,Application 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会在调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">reinitialize()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">defineOptions()</code>:定义命令行启动选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">handleOption()</code>:响应相应的命令行选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">main()</code></li>\\n</ul>\\n\\n<h4 id=\\"7反初始化\\">7、反初始化</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的。<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code>,最后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code>。最后这个反初始化过程,在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 就是直接调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">uninitialize</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">uninitialize</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</h3>\\n\\n<h4 id=\\"1命令行选项设定\\">1、命令行选项设定</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的命令行选项有:<code class=\\"language-plaintext highlighter-rouge\\">log(l)</code>、<code class=\\"language-plaintext highlighter-rouge\\">dump(d)</code>、<code class=\\"language-plaintext highlighter-rouge\\">cirrus(c)</code>、<code class=\\"language-plaintext highlighter-rouge\\">middle(m)</code>、<code class=\\"language-plaintext highlighter-rouge\\">help(h)</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">OptionSet</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">options</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>设定日志级别(0 - 8,默认是 6,表示 info 级别)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"l\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Log level argument, must be beetween 0 and 8 : \\\\\\n nothing, fatal, critic, error, warn, note, info, debug, trace. \\\\\\n Default value is 6 (info), all logs until info level are displayed.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">argument</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"level\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>其他一些选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"d\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Enables packet traces in logs. Optional arguments \\\\\\n are 'middle' or 'all' respectively to displays just middle packet \\\\\\n process or all packet process. If no argument is given, just outside \\\\\\n packet process will be dumped.\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"middle|all\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"c\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Cirrus address to activate a 'man-in-the-middle' \\\\\\n developer mode in bypassing flash packets to the official cirrus \\\\\\n server of your choice, it's a instable mode to help Cumulus developers, \\\\\\n </span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\">p2p.rtmfp.net:10000</span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\"> for example. By adding the 'dump' argument, \\\\\\n you will able to display Cirrus/Flash packet exchange in your logs \\\\\\n (see 'dump' argument).\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"address\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"m\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"Enables a 'man-in-the-middle' developer mode \\\\\\n between two peers. It's a instable mode to help Cumulus developers. \\\\\\n By adding the 'dump' argument, you will able to display Flash/Flash \\\\\\n packet exchange in your logs (see 'dump' argument).\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>显示帮助信息的选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"h\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Displays help information about command-line usage.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">OptionSet</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::OptionSet</code>,调用addOption可以向其中增加选项Option。其中required和repeatable表示:</p>\\n\\n<p>Sets whether the option is required (flag == true) or optional (flag == false).\\nReturns true if the option can be specified more than once, or false if at most once.\\n当需要显示帮助信息时,调用如下函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">displayHelp</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">HelpFormatter</span> <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setCommand</span><span class=\\"p\\">(</span><span class=\\"n\\">commandName</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setUsage</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"OPTIONS\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setHeader</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer, open source RTMFP server\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">cout</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setCommand()</code>: Sets the command name.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setUsage()</code>: Sets the usage string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setHeader()</code>: Sets the header string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">format()</code>: Writes the formatted help text to the given stream.</li>\\n</ul>\\n\\n<h4 id=\\"2处理命令行选项\\">2、处理命令行选项</h4>\\n\\n<p>参数是选项名和选项值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是帮助:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_helpRequested</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">URI</span> <span class=\\"n\\">uri</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"rtmfp://\\"</span><span class=\\"o\\">+</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">SocketAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getHost</span><span class=\\"p\\">(),</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getPort</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' : the exchange will bypass to '%s'\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' error : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">message</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是dump日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"all\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">ALL</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">MIDDLE</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">EXTERNAL</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是log,表示设定日志级别:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLevel</span><span class=\\"p\\">(</span><span class=\\"n\\">atoi</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">()));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"6dump-logs\\">6、Dump logs</h4>\\n\\n<p>先加一个作用域锁,然后再向日志流写数据。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">dumpHandler</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">cout</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>调用 manageLogFile,主要做一些日志大小超出限制的处理。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">manageLogFile</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先判断是否超过日志文件的大小上线,LOG_SIZE是1000000字节(即约 1 MB)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">getSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">LOG_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">num</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>打开新日志文件:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span> <span class=\\"nf\\">file</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"10\\"</span><span class=\\"p\\">);</span>\\n\\n</code></pre></div></div>\\n\\n<p>如果该文件已经存在,则先删除:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">remove</span><span class=\\"p\\">();</span>\\n\\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">--</span><span class=\\"n\\">num</span> <span class=\\"o\\">&gt;=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">renameTo</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span> <span class=\\"o\\">+</span> <span class=\\"mi\\">1</span><span class=\\"p\\">));</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span> \\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3停止运行\\">3、停止运行</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code>,该类中有纯虚函数 <code class=\\"language-plaintext highlighter-rouge\\">kill()</code> 需要被实现,于是有:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">kill</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">terminate</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code> 的定义在 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller.h</code> 中,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ApplicationKiller</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n \\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">kill</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4载入配置\\">4、载入配置</h4>\\n\\n<p>在initialize()函数中调用,上一篇已提到过。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">path</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5处理日志\\">5、处理日志</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">logHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">TID</span> <span class=\\"n\\">threadId</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">threadName</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Priority</span> <span class=\\"n\\">priority</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">filePath</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">long</span> <span class=\\"n\\">line</span><span class=\\"p\\">,</span> \\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">text</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>作用域锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">Path</span> <span class=\\"nf\\">path</span><span class=\\"p\\">(</span><span class=\\"n\\">filePath</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">string</span> <span class=\\"n\\">file</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getExtension</span><span class=\\"p\\">()</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"lua\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">directory</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">depth</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是命令行交互模式(即不是 daemon 模式):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_isInteractive</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">printf</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"%s %s[%ld] %s</span><span class=\\"se\\">\\\\n</span><span class=\\"s\\">\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">],</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getBaseName</span><span class=\\"p\\">()).</span><span class=\\"n\\">c_str</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">line</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">text</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>向日志流输出一句日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">DateTimeFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">LocalDateTime</span><span class=\\"p\\">(),</span><span class=\\"s\\">\\"%d/%m %H:%M:%S.%c \\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">]</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'\\\\t'</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadName</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'('</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadId</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\")</span><span class=\\"se\\">\\\\t</span><span class=\\"s\\">\\"</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getFileName</span><span class=\\"p\\">())</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'['</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">line</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\"] \\"</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">text</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">endl</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>日志文件的善后处理(主要处理文件大小限制可能产生的问题):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中真正启动的是 <code class=\\"language-plaintext highlighter-rouge\\">server</code>,它继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code>,而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code> 又继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Gateway</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Handler</code>。而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code> 继承自 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Runnable</code>,所以其是一个可以运行的线程。在 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 中,这是主线程。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span> <span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n<span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/Server.h</code> 中定义的,其构造函数的原型为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>个参数含义如下:</p>\\n\\n<blockquote>\\n <p>The Path Root for the Server Application Killer for Termanting the Server Application Server Configuration</p>\\n</blockquote>\\n\\n<p>距离来说,在我的 Worksapce 中:</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">root</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 构造函数的初始化列表极长:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"o\\">::</span><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">)</span> \\n <span class=\\"o\\">:</span> <span class=\\"n\\">_blacklist</span><span class=\\"p\\">(</span><span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"blacklist\\"</span><span class=\\"p\\">,</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_applicationKiller</span><span class=\\"p\\">(</span><span class=\\"n\\">applicationKiller</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_hasOnRealTime</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pService</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">luaMail</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"o\\">=</span><span class=\\"n\\">Script</span><span class=\\"o\\">::</span><span class=\\"n\\">CreateState</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.host\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"localhost\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">SMTPSession</span><span class=\\"o\\">::</span><span class=\\"n\\">SMTP_PORT</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.timeout\\"</span><span class=\\"p\\">,</span><span class=\\"mi\\">60</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>下面调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::File</code> 创建目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">((</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">WWWPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"www\\"</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>因为 <code class=\\"language-plaintext highlighter-rouge\\">roor</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 目录,所以 <code class=\\"language-plaintext highlighter-rouge\\">WWWPath</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/www</code> 目录。然后初始化 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code>,这个 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code> 是和 Lua 有关的东东,这里暂不细说,先知道与 Lua 相关就好。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Service</span><span class=\\"o\\">::</span><span class=\\"n\\">InitGlobalTable</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>下面就涉及到了 Lua script 了:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SCRIPT_BEGIN</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">SCRIPT_CREATE_PERSISTENT_OBJECT</span><span class=\\"p\\">(</span><span class=\\"n\\">Invoker</span><span class=\\"p\\">,</span><span class=\\"n\\">LUAInvoker</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">readNextConfig</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"n\\">configurations</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">lua_setglobal</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"cumulus.configs\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">SCRIPT_END</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN</code>、<code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_CREATE_PERSISTENT_OBJECT和SCRIPT_END</code> 都是宏,其定义在 <code class=\\"language-plaintext highlighter-rouge\\">Script.h</code> 文件中,如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define SCRIPT_BEGIN(STATE) \\\\\\n if (lua_State* __pState = STATE) { \\\\\\n const char* __error=NULL;\\n \\n#define SCRIPT_CREATE_PERSISTENT_OBJECT(TYPE,LUATYPE,OBJ) \\\\\\n Script::WritePersistentObject&lt;TYPE,LUATYPE&gt;(__pState,OBJ); \\\\\\n lua_pop(__pState,1);\\n \\n#define SCRIPT_END }\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN和SCRIPT_END</code> 经常用到,当与 Lua 相关的操作出现时,都会以这两个宏作为开头和结尾。</p>\\n\\n<h4 id=\\"2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServerParams</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">params</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 正在运行,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer server is yet running, call stop method before\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定端口号,如果端口号为 0,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must have a positive value\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusEdge</code> 的端口号,如果其端口号与 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusSever</code> 端口号相同,则返回并终止启动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"n\\">_edgesPort</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must different than RTMFPServer edges.port\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>Cirrus:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">2000000</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// 2 sec by default</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">Target</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// no waiting, direct process in the middle case!</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode with server %s \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pCirrus</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">address</span><span class=\\"p\\">.</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode between peers \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>UDP Buffer:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span> <span class=\\"o\\">?</span> \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">getReceiveBufferSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Socket buffer receving/sending size = %u/%u\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">threadPriority</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>启动线程,进入循环运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>上句具体的源码实现为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果在运行则返回并终止启动。然后加一个局部锁。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果不得不join()到主线程中,那就join()吧</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后就运行这个线程吧:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3回顾一下整个启动流程\\">3、回顾一下整个启动流程</h4>\\n\\n<p>到此我们先回顾一下启动过程:</p>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的启动入口 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数开始,创建 <code class=\\"language-plaintext highlighter-rouge\\">Server</code> 对象并启动(调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 函数)。<code class=\\"language-plaintext highlighter-rouge\\">Server::start()</code> 中调用其父类(<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code>)的父类(<code class=\\"language-plaintext highlighter-rouge\\">Startable</code>)的方法 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 开启线程。\\n调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 函数后,开启线城时传入的参数为 <code class=\\"language-plaintext highlighter-rouge\\">*this</code>,所以就会运行 <code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code>;</p>\\n\\n<h4 id=\\"4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code> 调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,但这个函数被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖,所以会运行 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>,其源码如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果CumulusEdge:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP edges server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">onStart</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>运行线程:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>处理异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果跳出了,则终止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">onStop</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server stops\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>该函数内部又会调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,该函数调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>它是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 实现。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 会调用 <code class=\\"language-plaintext highlighter-rouge\\">void run(const volatile bool&amp; terminate)</code> 方法,该方法被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">...</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</title>\\n \\t<meta name=\\"description\\" content=\\"RTMFP 是 Adobe 开发的基于 UDP 协议的实时传输媒体流协议,支持 P2P 传输,具有较高的实时性和安全性。它的主要应用场景是视频通信、语音通信和网络游戏。OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。Cumulus 是一个基于 OpenRTMFP 的服务器,提供 RTMFP 服务。它具有轻量级、跨平台和可扩展的特点,并且还提供了负载均衡和可扩展性解决方案。YY 语音的 Web 端音视频流媒体能力,正是基于 RTMFP 协议做的迭代优化实现的。本文是船长关于这个系列文章的第一篇。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-09T18:57:19+00:00\\" class=\\"by-line\\">09 Apr 2012, 广州 | 作者 麦克船长 | 总计 8401 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfp是什么\\" id=\\"markdown-toc-一rtmfp是什么\\">一、RTMFP 是什么?</a> <ul>\\n <li><a href=\\"#文件分享-p2p-和实时流媒体-p2p-的区别是什么\\" id=\\"markdown-toc-文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</a></li>\\n <li><a href=\\"#rtmfp-和-rtmp-之间的区别是什么\\" id=\\"markdown-toc-rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</a></li>\\n <li><a href=\\"#flash-player-支持-rtmfp-吗\\" id=\\"markdown-toc-flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</a></li>\\n <li><a href=\\"#cumulus-使用-adobe-的-cirrus-key-吗\\" id=\\"markdown-toc-cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</a></li>\\n <li><a href=\\"#这个开源项目合法吗\\" id=\\"markdown-toc-这个开源项目合法吗\\">这个开源项目合法吗?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二openrtmfp和cumulus\\" id=\\"markdown-toc-二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</a></li>\\n <li><a href=\\"#三入门介绍与部署cumulusserver\\" id=\\"markdown-toc-三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</a> <ul>\\n <li><a href=\\"#1背景介绍\\" id=\\"markdown-toc-1背景介绍\\">1、背景介绍</a></li>\\n <li><a href=\\"#2准备工作\\" id=\\"markdown-toc-2准备工作\\">2、准备工作</a></li>\\n <li><a href=\\"#3安装\\" id=\\"markdown-toc-3安装\\">3、安装</a> <ul>\\n <li><a href=\\"#31外部依赖的安装\\" id=\\"markdown-toc-31外部依赖的安装\\">3.1、外部依赖的安装</a></li>\\n <li><a href=\\"#32安装openrtmfpcumulus\\" id=\\"markdown-toc-32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#4配置\\" id=\\"markdown-toc-4配置\\">4、配置</a></li>\\n <li><a href=\\"#5启动\\" id=\\"markdown-toc-5启动\\">5、启动</a></li>\\n <li><a href=\\"#6基本使用\\" id=\\"markdown-toc-6基本使用\\">6、基本使用</a></li>\\n <li><a href=\\"#7扩展cumulusserverserverapplication\\" id=\\"markdown-toc-7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三用lua编写helloworld应用扩展cumulusserver\\" id=\\"markdown-toc-三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</a> <ul>\\n <li><a href=\\"#1server-side\\" id=\\"markdown-toc-1server-side\\">1、Server-side</a> <ul>\\n <li><a href=\\"#11serverconfiguration\\" id=\\"markdown-toc-11serverconfiguration\\">1.1、Server configuration</a></li>\\n <li><a href=\\"#12applicationfile\\" id=\\"markdown-toc-12applicationfile\\">1.2、Application file</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2client-side\\" id=\\"markdown-toc-2client-side\\">2、Client-side</a></li>\\n <li><a href=\\"#3运行结果\\" id=\\"markdown-toc-3运行结果\\">3、运行结果</a></li>\\n <li><a href=\\"#4远程测试一个免费的测试服务器\\" id=\\"markdown-toc-4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<h3 id=\\"一rtmfp是什么\\">一、RTMFP 是什么?</h3>\\n\\n<p>Real-Time Media Flow Protocol(RTMFP)是 Adobe 开发的一种基于 UDP 并支持 P2P 的实时传输媒体流。主要特点是:高传输效率(可以使用压缩和算法来优化流量从而提高传输效率)、高实时性(可以保证媒体流的实时性使得视频通信和其他实时通信更加流畅)、支持 P2P 传输(减少对服务器的依赖从而减少带宽和服务器资源消耗)、高安全性(加密媒体流从而保证其安全性)。</p>\\n\\n<p>RTMFP 的主要应用场景包括:视频通信(视频聊天和视频会议)、语音通信(语音聊天、电话)、网络游戏。不过 RTMFP 目前仅有 Adobe 开发的版本,所以它并不是个开源项目,而是个商业化服务。那么有没有开源版本呢?</p>\\n\\n<h4 id=\\"文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</h4>\\n\\n<p>RTMFP 是一个 P2P 系统,但它仅针对实时通信时直接用户到用户之间的通信而设计,不能用于多个对等方之间进行文件共享(使用分段下载)。Facebook 在其 Pipe 应用中使用此协议将大文件直接在两个用户之间传输。</p>\\n\\n<h4 id=\\"rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</h4>\\n\\n<p>RTMP 是实时消息协议,RTMFP 代表实时媒体流协议。RTMFP 基于用户数据报协议(UDP),而 RTMP 基于传输控制协议(TCP)。\\n与 RTMP 不同,RTMFP 还支持直接从一个 Adobe Flash Player 传输数据到另一个,而无需经过服务器。</p>\\n\\n<h4 id=\\"flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</h4>\\n\\n<p>RTMFP 是基于用户数据报协议(UDP)的,而 RTMP 是基于传输控制协议(TCP)的。与 RTMP 不同,RTMFP 还支持直接在两个 Adobe Flash Player 之间发送数据,而不经过服务器。Flash Player 10.0 仅允许一对一通信进行 P2P,但从 10.1 开始允许应用程序级别的多播。Flash Player 查找适当的分发路由(覆盖网络),并可以将其分发到通过 P2P 连接的组。</p>\\n\\n<h4 id=\\"cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</h4>\\n\\n<p>不!当然,这是Cumulus的主要目标:成为Cirrus GPL的替代品。唯一的限制是:你的CPU,内存和单台机器的端口数。</p>\\n\\n<h4 id=\\"这个开源项目合法吗\\">这个开源项目合法吗?</h4>\\n\\n<p>在美国,数字千年版权法(Digital Millennium Copyright Act)规定,逆向工程用于协议互操作性是合法的。你可以在 WikiPedia 上查看相关讨论:</p>\\n\\n<ul>\\n <li>http://en.wikipedia.org/wiki/Real_Time_Media_Flow_Protocol</li>\\n <li>http://en.wikipedia.org/wiki/Proprietary_protocol</li>\\n <li>http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act</li>\\n</ul>\\n\\n<p>当逆向工程的目的是协议互操作性时,有法律先例。在美国,数字千年版权法(Digital Millennium Copyright Act)为逆向工程软件以使其与其他软件互操作提供了安全保障。</p>\\n\\n<h3 id=\\"二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</h3>\\n\\n<p>OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。它包含了 RTMFP 协议的实现,以及一些额外的功能,如媒体流传输、P2P 通信、脚本引擎和数据存储。</p>\\n\\n<p>Cumulus 是一个基于 OpenRTMFP 的服务器,是一个完整的开源且跨平台的 RTMFP 服务器,可通过脚本进行扩展。CumulusServer 根据 GPL 许可在考虑以下 4 个概念的情况下开发:速度、轻量、跨平台和可扩展。尽管尚未发布版本,但只有在 CumulusServer 经过测试和批准后才会将代码推送到 github。实际上,主要稳定的功能有:</p>\\n\\n<ul>\\n <li>P2P rendez-vous service</li>\\n <li>live streaming</li>\\n <li>RPC、pull、push exchange,实际上客户端和服务器之间的所有 AMF 可能交换</li>\\n <li>脚本引擎,用于创建自己的应用服务器或扩展 Cumulus 的功能</li>\\n <li>可扩展性和负载均衡解决方案</li>\\n</ul>\\n\\n<p>下面的内容是本篇 blog 的重点,包括两部分:先是 OpenRTMFP 应用的核心 CumulusServer 的入门介绍与部署,然后用 Lua 编写 HelloWorld 应用扩展 CumulusServer,我们开始吧!</p>\\n\\n<h3 id=\\"三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</h3>\\n\\n<h4 id=\\"1背景介绍\\">1、背景介绍</h4>\\n\\n<p>OpenRTMFP 可以帮助你实现 Flash 的实时应用的高并发扩展,OpenRTMFP/Cumulus 是基于 GNU General Public License 的。</p>\\n\\n<p>POCO:POrtable COmponents,是一个强大的开源 C++ 库。其在 C++ 开发中的角色,相当于 Java Class Library、苹果的 Cocoa、.NET framework。</p>\\n\\n<h4 id=\\"2准备工作\\">2、准备工作</h4>\\n\\n<p>下载:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><strong>External Dependencies</strong></th>\\n <th><strong>Official Site</strong></th>\\n <th><strong>Windows</strong></th>\\n <th><strong>Linux/OSX</strong></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>OpenSSL</td>\\n <td><a href=\\"http://www.slproweb.com/products/Win32OpenSSL.html\\">Official Site</a></td>\\n <td><a href=\\"http://www.slproweb.com/download/Win32OpenSSL_Light-1_0_1.exe\\">Download</a></td>\\n <td><a href=\\"http://www.openssl.org/source/openssl-1.0.1.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>Lua</td>\\n <td><a href=\\"http://www.lua.org/\\">Official Site</a></td>\\n <td><a href=\\"http://luaforwindows.googlecode.com/files/LuaForWindows_v5.1.4-45.exe\\">Download</a></td>\\n <td><a href=\\"http://www.lua.org/ftp/lua-5.1.5.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>POCO</td>\\n <td><a href=\\"http://pocoproject.org/\\">Official Site</a></td>\\n <td><a href=\\"http://downloads.sourceforge.net/project/poco/sources/poco-1.4.3/poco-1.4.3p1.zip\\">Download</a></td>\\n <td><a href=\\"https://sourceforge.net/projects/poco/files/sources/poco-1.4.3/poco-1.4.3p1.tar.gz/download\\">Download</a></td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>注意:POCO for linux 版本必须是 1.4.0 或更高,否则会引起 TCP 相关的 bug。</p>\\n\\n<h4 id=\\"3安装\\">3、安装</h4>\\n\\n<h5 id=\\"31外部依赖的安装\\">3.1、外部依赖的安装</h5>\\n\\n<p>Windows 下略,Linux 下基本就是:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>./configuremakesudo\\n<span class=\\"nv\\">$ </span>make <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</h5>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>OpenRTMFP-Cumulus/CumulusLib\\n<span class=\\"nv\\">$ </span>make\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd</span> ../CumulusServer\\n<span class=\\"nv\\">$ </span>make\\n</code></pre></div></div>\\n\\n<p>如果出现了 <code class=\\"language-plaintext highlighter-rouge\\">.h</code> 文件、lib 库找不到的情况,请修改 OpenRTMFP-Cumulus/CumulusLib/Makefile 或 OpenRTMFP-Cumulus/CumulusServer/Makefile。</p>\\n\\n<h4 id=\\"4配置\\">4、配置</h4>\\n\\n<p>通过编写 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP-Cumulus/CumulusServer/CumulusServer.ini</code> 文件来为 OpenRTMFP-Cumulus 进行个性化配置(默认是没有这个文件的),这个文件的内容形如:</p>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1985</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span>\\n<span class=\\"n\\">name</span><span class=\\"o\\">=</span><span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span><span class=\\"o\\">=</span><span class=\\"n\\">C</span><span class=\\"p\\">:</span><span class=\\"o\\">/</span><span class=\\"n\\">CumulusServer</span><span class=\\"o\\">/</span><span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<p>一些字段的设置含义如下,摘自:<a href=\\"https://github.com/OpenRTMFP/Cumulus/wiki/Installation\\">地址</a>。</p>\\n\\n<ul>\\n <li>公开给 Client 的端口号 <code class=\\"language-plaintext highlighter-rouge\\">port</code>,默认值是 1935(RTMFP 服务器的默认端口),用于 CumulusServer 监听 RTMFP 请求。</li>\\n <li>UDP 缓冲区字节数 <code class=\\"language-plaintext highlighter-rouge\\">udpBufferSize</code>, allows to change the size in bytes of UDP reception and sending buffer. Increases this value if your operating system has a default value too lower for important loads.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAliveServer</code>, time in seconds for periodically sending packets keep-alive with server, 15s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAlivePeer</code>, time in seconds for periodically sending packets keep-alive between peers, 10s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.activated</code>, activate or not the edges server on the RTMFP server (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, CumulusServer stays a RTMFP server without edges ability (default value is false).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.port</code>, port for the edges server, to accept incoming new CumulusEdge instances (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, it’s the port 1936.</li>\\n</ul>\\n\\n<blockquote>\\n <p>Warning: This port will receive plain text request from edges, for this purpose it should not be made public. It’s very important for security consideration. It must be available only for CumulusEdge instances, and anything else.</p>\\n</blockquote>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.attemptsBeforeFallback</code>, number of CumulusEdge attempt connections before falling back to CumulusServer (see CumulusEdge, Scalability page for more details about CumulusEdge). By default the value is 2 (in practical, 2 attempts happens after 5 sec approximately).</li>\\n <li>SMTP IP 地址 <code class=\\"language-plaintext highlighter-rouge\\">smtp.host</code>, configure a SMTP host to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is localhost.</li>\\n <li>SMTP 端口 <code class=\\"language-plaintext highlighter-rouge\\">smtp.port</code>, configure a SMTP port to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 25.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">smtp.timeout</code>, configure a SMTP timeout session in seconds to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 60 seconds.</li>\\n <li>日志路径 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code>,默认是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/logsby</code>。</li>\\n <li>日志文件名称 <code class=\\"language-plaintext highlighter-rouge\\">logs.name</code>,默认是<code class=\\"language-plaintext highlighter-rouge\\">log</code>。</li>\\n</ul>\\n\\n<h4 id=\\"5启动\\">5、启动</h4>\\n\\n<p>Windows 下的启动方法为:</p>\\n\\n<pre><code class=\\"language-dos\\">$ CumulusServer.exe /registerService [/displayName=CumulusServer /description=\\"Open Source RTMFP Server\\" /startup=automatic]\\n</code></pre>\\n\\n<p>Unix-like 下的启动方法为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"o\\">[</span><span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>/var/run/CumulusServer.pid]\\n</code></pre></div></div>\\n\\n<p>具体地,我的启动命令为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>./CumulusServer.pid\\n</code></pre></div></div>\\n\\n<h4 id=\\"6基本使用\\">6、基本使用</h4>\\n\\n<p>本地 Flash client 可以通过如下语句连接:</p>\\n\\n<div class=\\"language-as highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">$</span> <span class=\\"kd\\">var</span> <span class=\\"nx\\">nc</span><span class=\\"o\\">:</span><span class=\\"nx\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nx\\">NetConnection</span><span class=\\"p\\">()</span><span class=\\"o\\">;</span><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost/\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>RTMFP默认是采用1935端口,如果你特别指定了其他端口,比如12345,请使用如下方式:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>nc.connect(\\"rtmfp://localhost:12345/\\");\\n</code></pre></div></div>\\n\\n<h4 id=\\"7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</h4>\\n\\n<p>启动CumulusServer后,会在可执行文件的目录下出现一个www目录,该目录的作用,就是作为 Server Application 的默认根目录。具体的对应关系如下:</p>\\n\\n<blockquote>\\n <p>rtmfp://host:port/ -&gt; [CumulusServer folder]/www/main.lua (root application)</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/myApplication -&gt; [CumulusServer folder]/www/myApplication/main.lua</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/Games/myGame -&gt; [CumulusServer folder]/www/Games/myGame/main.lua</p>\\n</blockquote>\\n\\n<p>另外要提醒的是,如果main.lua文件被修改,则不需要重启 CumulusServer,因为 Server Application 的创建是一种动态的方式。</p>\\n\\n<h3 id=\\"三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</h3>\\n\\n<p>下面的这个实例是在本地(Client 与 Server 位于同一机器上)测试的。</p>\\n\\n<h4 id=\\"1server-side\\">1、Server-side</h4>\\n\\n<h5 id=\\"11serverconfiguration\\">1.1、Server configuration</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span> <span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1935</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span><span class=\\"n\\">name</span> <span class=\\"o\\">=</span> <span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12applicationfile\\">1.2、Application file</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">function</span> <span class=\\"nf\\">onConnection</span><span class=\\"p\\">(</span><span class=\\"n\\">client</span><span class=\\"p\\">,</span><span class=\\"n\\">response</span><span class=\\"p\\">,</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">function</span> <span class=\\"nf\\">client</span><span class=\\"p\\">:</span><span class=\\"n\\">test</span><span class=\\"p\\">(</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">firstname</span> <span class=\\"o\\">=</span> <span class=\\"n\\">unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">arg</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"s2\\">\\"Hello \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">firstname</span><span class=\\"o\\">..</span><span class=\\"s2\\">\\" \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">name</span>\\n <span class=\\"k\\">end</span>\\n <span class=\\"k\\">end</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2client-side\\">2、Client-side</h4>\\n\\n<div class=\\"language-java highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\">// CumulusClient.as</span>\\n\\n<span class=\\"kn\\">package</span> <span class=\\"err\\">{</span>\\n <span class=\\"nn\\">import</span> <span class=\\"n\\">flash</span><span class=\\"o\\">.</span><span class=\\"na\\">display</span><span class=\\"o\\">.</span><span class=\\"na\\">Sprite</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetConnection</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetStream</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.Responder</span><span class=\\"o\\">;</span>\\n\\n <span class=\\"kd\\">public</span> <span class=\\"kd\\">class</span> <span class=\\"nc\\">CumulusClient</span> <span class=\\"kd\\">extends</span> <span class=\\"nc\\">Sprite</span> <span class=\\"o\\">{</span>\\n <span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">nc:</span><span class=\\"nc\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t<span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">ns:</span><span class=\\"nc\\">NetStream</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t\\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">CumulusClient</span><span class=\\"o\\">()</span> <span class=\\"o\\">{</span>\\n <span class=\\"n\\">nc</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nc\\">NetConnection</span><span class=\\"o\\">();</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">connect</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"rtmfp://localhost\\"</span><span class=\\"o\\">);</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">client</span> <span class=\\"o\\">=</span> <span class=\\"k\\">this</span><span class=\\"o\\">;</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">call</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"test\\"</span><span class=\\"o\\">,</span><span class=\\"k\\">new</span> <span class=\\"nc\\">Responder</span><span class=\\"o\\">(</span><span class=\\"n\\">onResult</span><span class=\\"o\\">,</span><span class=\\"n\\">onStatus</span><span class=\\"o\\">),</span> <span class=\\"s\\">\\"OpenRTMFP/Cumulus\\"</span><span class=\\"o\\">,</span> <span class=\\"s\\">\\"World\\"</span><span class=\\"o\\">)</span>\\n <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">close</span><span class=\\"o\\">():</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span> \\n\\t\\t\\t<span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">close</span><span class=\\"o\\">();</span>\\n \\t<span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onStatus</span><span class=\\"o\\">(</span><span class=\\"nl\\">status:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">status</span><span class=\\"o\\">.</span><span class=\\"na\\">description</span><span class=\\"o\\">)</span>\\n\\t <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onResult</span><span class=\\"o\\">(</span><span class=\\"nl\\">response:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">response</span><span class=\\"o\\">)</span> <span class=\\"c1\\">// expected to display \\"Hello World OpenRTMFP/Cumulus\\" </span>\\n\\t <span class=\\"o\\">}</span> \\n\\t<span class=\\"o\\">}</span>\\n<span class=\\"o\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3运行结果\\">3、运行结果</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Hello World OpenRTMFP/Cumulus\\n[SWF] CumulusClient.swf - 解压缩后为 1,776 个字节\\n[卸装 SWF] CumulusClient.swf\\n</code></pre></div></div>\\n\\n<h4 id=\\"4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</h4>\\n\\n<p>获取 Developer Key 的地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/index.jsp\\n</code></pre></div></div>\\n\\n<p>服务器配置信息:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Server: amd64 OS: Linux 2.6.18-028stab095.1\\nServer IP: 108.59.252.39\\nOpenRTMFP as of: 22.Feb.2012\\n</code></pre></div></div>\\n\\n<p>编写服务器段应用地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/manage_ssls.jsp\\n</code></pre></div></div>\\n\\n<p>快去试试吧 :)</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"thinking":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>不要船开远了,就忘了为什么启航</title>\\n \\t<meta name=\\"description\\" content=\\"2020 年的 6 月 4 日我入职阿里巴巴集团,7 天后的 6 月 11 日我写下了这篇文章。偶然翻到了当时这篇文章,遂转录于此,提醒自己勿忘初心。在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:很多人是带着梦想来阿里的,那么我的梦想是什么呢?最喜欢新六脉的哪句话?为什么?关于阿里企业价值观:为什么要接受这套价值观?价值观的本质意义(极度务实视角)是什么?Landing 的 SOP;问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>不要船开远了,就忘了为什么启航</h2>\\t\\t\\n\\t<time datetime=\\"2022-08-11T15:53:57+00:00\\" class=\\"by-line\\">11 Aug 2022, 杭州 | 作者 麦克船长 | 总计 3223 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>偶然翻到 2020.06.11 刚来到阿里时写的一篇内容(我是 2020 年的 6 月 4 日我入职阿里巴巴集团),是有关于来阿里的期待、对这家公司的一些粗浅初步的理解。此时再翻来看看,最大的感触就是,提醒自己勿忘初心。</p>\\n\\n<p>在不涉及到公司数据安全及商业机密问题的前提下,稍做了一些删改,发布在这里作为一个回顾。本次穿插了一些图片,当时写的时候还没有这些照片。本文内容包括:</p>\\n\\n<ul>\\n <li>很多人是带着梦想来阿里的,那么我的梦想是什么呢?</li>\\n <li>最喜欢新六脉的哪句话?为什么?</li>\\n <li>关于阿里企业价值观:为什么要接受这套价值观?</li>\\n <li>价值观的本质意义(极度务实视角)是什么?</li>\\n <li>Landing 的 SOP</li>\\n <li>问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2020 年平安夜 · 百年湖畔 87 期合影</p>\\n\\n<h3 id=\\"很多人是带着梦想来阿里的那么我的梦想是什么呢\\">很多人是带着梦想来阿里的,那么我的梦想是什么呢?</h3>\\n\\n<p>Christensen 在《创新者的窘境》中提到:每一次技术更迭,都需要破坏性创新,而破坏性创新在前一次技术更迭的胜出者内部是很难生长出来的。阿里诞生以来,不断地创造第二增长曲线:阿里巴巴、淘宝、支付宝、天猫、阿里云、钉钉 …… 这让我非常好奇。其中很多产品穿越多个时间周期,期间不断创造内生二次曲线。</p>\\n\\n<p>但是阿里也一样错失了很多,微信、美团、拼多多、抖/快…… 等等很多产品诞生在了其他公司,还有某些产品在不断的科技更迭中自身生长出了第二曲线。</p>\\n\\n<p>因此我来阿里的梦想也非常明确:<strong>参与或创造一次(甚至多次)第二曲线,可以是新产品,也可以是原有产品内生的。在这个过程中获得个人成长、个人价值。</strong></p>\\n\\n<p>一直以来,我有三个最想实现或得到的东西:LOVE、CREATION、FREEDOM。随着生活与工作的前行,对这三者的理解,在不断加深。在这个问题里,我想应该是讨论”CREATION”。</p>\\n\\n<p>CREATION 上,我的梦想的范式,大概是从自己中学时代就确立了,在某一次人类社会变革浪潮中,扮演有一定权重的角色。这里面有几个变量:<strong>什么领域(F)的变革;什么规模(S)的变革;多大的权重(W);什么角色(R)。</strong></p>\\n\\n<p>F 这个变量,我在中学及大学时代逐渐明确,是以相对普适的产品形式输出结果并对社会变革产生积极作用。后来越来越明确为科技与商业结合的领域。</p>\\n\\n<p>S、W 这两个变量,自然是越大越好。因此我会希望能够构建尽可能大的机会,或者参与到尽可能大的机会中。R 希望是有强烈 Ownership 的身份。</p>\\n\\n<p>因此过去几年我选择了创业。创业就像冲浪,你抓住一次浪并完成漂亮的动作,就是一次不算失败的创业。但是如果一个浪没抓住,你去追它是没意义的,而应该等待下一个浪。我认为在未来 5~10 年内难以出现规模能大到令我足够兴奋的科技浪潮。大浪潮中属于创业者的大机会很多,而中小浪潮的大机会基本只属于大平台,那么为了在壮年期做获得我的 CREATION,我选择了加入阿里这样的大平台。</p>\\n\\n<p>在最后做决定以及初来阿里的那个人生转折点,作为老阿里人的曲洋老师对我说的一句话,深深地鼓励了我,他说:”带着创业气质,把这里当你的舞台折腾!”</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2020 年双十一 · 淘宝 KO</p>\\n\\n<h3 id=\\"最喜欢新六脉的哪句话为什么\\">最喜欢新六脉的哪句话?为什么?</h3>\\n\\n<p>最喜欢的是“因为信任所以简单”。</p>\\n\\n<p>我一直认为人最重要的两个元特质是”真实”和“谦逊”,由”真实”可以塑造自我(对内)、构建信任(对外),后者可以带来清晰的边界,继而实现人与人之间高效的互动(这种互动包括各种人际关系在内,如婚姻、合伙、共事、合作等等)。</p>\\n\\n<p><strong>事物(虚实皆可)呈现在人的认知中,会得到三方面的投影:facts、opinion、feeling。如果我们足够真实,当我们需要把这三方面呈现给他人时,双方就能顺畅建立信任。信任的结果,就对应到这三方面:彼此之间建立共识(facts)、求同存异(opinion)、尊重感受(feeling),这就是”简单”。</strong></p>\\n\\n<p>另外一句是鼓励自己勇于绽放的一条:「此次此刻,非我莫属」。</p>\\n\\n<p>激情、自信、积极…… 通常行为统一表现为“勇于绽放自己”,绽放有表达(语言)与投身(行为)两种表现形式。更进一步推进就是”此次此刻,非我莫属”的阿里价值观。</p>\\n\\n<p>低调、稳重、谦逊,其实与“此次此刻,非我莫属“,并不矛盾。这点是我来到阿里后,发现自己在过去这些年的创业中已经不知不觉改变了,从 Introvert 逐渐变成了 Extrovert 的人,而且从曾经 social 中消耗能量,逐渐变为我现在可以感知到获得能量。这种变化,是我最近来阿里才确认发生的,此前因为自己创业者的身份没有察觉这种变化的发生。</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-3.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年秋 · 径山之行</p>\\n\\n<h3 id=\\"关于阿里企业价值观为什么要接受这套价值观\\">关于阿里企业价值观:为什么要接受这套价值观?</h3>\\n\\n<p>马老师和老逍都提到这个:我们是寻找同路人,而不是教育别人。这其实非常明晰地解释了为什么阿里要构建一个毛细血管网络一样的政委体系。基于这种用人理念,政委体系不敢说是最优解,但一定是优解(而且是否有更优解的论证没有意义)。</p>\\n\\n<p>对于个人,我的理解是要做两件事:<strong>1)构建自己的价值观体系(初始化);2)寻找价值观契合的公司(做匹配)。这两点里,没有任何地方提到”你要改变价值观为了契合你所在的公司”。</strong></p>\\n\\n<p>而企业价值观呢,其实可以分两部分看待:普世价值观、独特价值观。前者因为是普世的,所以到了哪个公司这种价值观都对,这点老逍也提了,比如“客户第一”。后者是个性化的,但不存在孰高孰低,就像一个人内向还是外向,你不能说哪个是错的。</p>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-4.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年双十一 · 天天特卖团队</p>\\n\\n<h3 id=\\"价值观的本质意义极度务实视角是什么\\">价值观的本质意义(极度务实视角)是什么?</h3>\\n\\n<p><strong>在充分考虑价值观适配使命、愿景基础上,价值观本身的意义,在和风细雨时(即企业价值观与其他价值判断相 match 时),是看不到的。但在暴风骤雨时(即企业价值观与其他价值判断相冲突时),就能显示其实实在在的作用了。</strong>我认为包括三类,前两个是阿里整体视角,第三个是阿里内部:</p>\\n\\n<ul>\\n <li>经济体内,阿里与其他生态位的冲突或损益关系,如曾经的美蘑口一役。</li>\\n <li>经济体内,其他的生态位之间的冲突或损益关系,如曾经的十月围城。</li>\\n <li>阿里人的行为价值判断,如最近的钉钉代考事件、过往的各类廉政事件。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-5.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年冬 · 淘宝天猫合并前合影</p>\\n\\n<h3 id=\\"landing的sop\\">Landing 的 SOP</h3>\\n\\n<p>大家都说 landing 充满挑战,马老师其实给出了 landing 的 SOP 三部曲:<strong>一起打过仗、一起创过新、一起度过难。三个经历都 close 才算 smooth landing。</strong></p>\\n\\n<p>集团人才策略层面、HR 实操层面、Leader 层面、,对于新人 landing 能做到什么程度的保障,其实每个新人感受到的不尽相同:</p>\\n\\n<ul>\\n <li>集团层面,始终是在构建更好的新人 landing 环境的,这符合自身价值,这能打下很好的底子。</li>\\n <li>实操层面,包括面试阶段对候选人的价值观判断、预期管理,面试及入职后公司文化及人才体系的事实呈现、内化吸收和长期解惑。</li>\\n <li>Leader 层面,这是新人体感最强烈的部分,也是最重要的部分。尽管拥抱变化,但首先 Leader 需要给出尽可能最全面的考虑,其次是对候选人的预期管理。好的 Leader 会给候选人提供合理的着陆点、多个降落伞、缓冲垫,完成 smooth landing。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-06-11-captain-alibaba-6.png\\" alt=\\"image\\" /></p>\\n\\n<p>注:2021 年夏 · 出差厦漳泉</p>\\n\\n<h3 id=\\"问问自己来到阿里如果初期我可能需要做一点改变那会是什么\\">问问自己,来到阿里,如果初期我可能需要做一点改变,那会是什么?</h3>\\n\\n<p>曾经个人的激情与动力,常来自于“增长”。常说<strong>高增长掩盖一切</strong>,所以未来在阿里如果不能如创业般快速获得反馈得到积极结果,并且大平台中必然要接受大量关联方共同参与项目而导致的效率降低,因此我要逐渐改变自己,重新适应这种环境下的激情与动力获得方式。</p>\\n\\n<p>另一方面,作为创业公司的负责人,工作中鲜有因为内部原因而无法推进的事情,但是扮演肩部或腰部角色时,需要接受头部决策的一定程度不可控,这是我需要作出的适应与改变。关于这一点,我在几个月前就已经在做预期管理和心态调整,我认为以创业者的强适应性,这可能并不会是问题,但是我习惯于保持谨慎的乐观来面对自己。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</title>\\n \\t<meta name=\\"description\\" content=\\"阿里内部创业项目「天天特卖」招合伙人啦!以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。天天特卖期待你的加入!\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>欢迎成为「淘宝-天天特卖」团队的创业合伙人!</h2>\\t\\t\\n\\t<time datetime=\\"2021-11-11T19:59:43+00:00\\" class=\\"by-line\\">11 Nov 2021, 杭州 | 作者 麦克船长 | 总计 917 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2021-11-11-captain-tttm-1.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h3 id=\\"天天特卖团队理念\\">天天特卖团队理念</h3>\\n\\n<h4 id=\\"特卖合伙人\\">特卖合伙人</h4>\\n\\n<p>以「特卖合伙人」为基石的、以「使众人行」的战友感为人才基本要求、以「用人做事,而非做事用人」为人才建设核心,是天天特卖团队的组织管理理念。特卖核心管理团队每 Q 会进行一次班子建设通晒。</p>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-9.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h4 id=\\"如何理解协作\\">如何理解协作?</h4>\\n\\n<p>从长时间线来看,我们是为了不断积累信用,像一张信用卡一样,不断获得别人愿意支持我们的更大额度。不要事情结果还可以,而我们却没有积累到信用。互联网本质也是现代工业。而现代工业,一是社会分工,二是社会协作。想取得现代工业项目的结果,就要有更大的人才包容度、环境包容度。工作的结果就是在妥协与博弈中取得的,这是和光同尘的本质,也是现代工业复杂系统拿到结果的本质。只有这样我们才能让越来越多的人追随我们一起 do something,这种追随不一定只有上下级才是,而是愿意并且相信和我们能到达更远的地方。这背后的信用,要我们一步一个脚印地去积累,对他人给予的信任要保持敬畏、如履薄冰、懂得感恩。对每一段阶段性或长或短结束的合作,都要表达感谢。</p>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-8.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h4 id=\\"如何看待同学的优势及短板\\">如何看待同学的优势及短板?</h4>\\n\\n<ul>\\n <li>优势:讲优势有两个可能的目的,要么组织会在未来任务分配上重点考虑发挥该同学优势的事情,要告诉 TA,要激励 TA,是 TA 前行的自信来源之一。要么是对于同学也把握不准的特点,我们明确告诉 TA 这是你被我欣赏的优点。</li>\\n <li>短板:什么是要讲的短板?未来一段时间,最期待你补足提升的。一旦这方面显著进步,就会向上迈进很大一步,甚至可以突破自己当下成长的瓶颈。要花多少篇幅讲?要比优势,有更大篇幅去讲。讲完就结束了么?对这个短板,一定要表达态度,也一定要对是否有方法、什么方法来补足短板要和同学沟通。</li>\\n <li>无论是优势,还是短板,要说到点儿上,不要说片儿汤话。要让同学们能够引起思考、启发的。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-10.jpg\\" alt=\\"imagee\\" /></p>\\n\\n<h3 id=\\"天天特卖期待你的加入\\">天天特卖期待你的加入!</h3>\\n\\n<p>新天天特卖缘起于「手淘下沉市场战役)」,于 2021 年初上线,以「极致性价比货源、裸价直降、全网比价、买贵必赔」打造手淘极致价格敏感人群的购物阵地。目前天天特卖团队有行业运营、用户运营、数据策略、整合营销、直播运营、内容运营等岗位,有兴趣的同学可以钉钉随时找我,期待你的加入!</p>\\n\\n<h4 id=\\"欢迎添加我的微信sinosuperman-推荐自荐--\\">欢迎添加我的微信:sinosuperman 推荐、自荐 ^ ^</h4>\\n\\n<p><img src=\\"/img/src/2021-11-11-captain-tttm-11.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-2.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-3.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-4.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-5.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-6.jpg\\" alt=\\"imagee\\" />\\n<img src=\\"/img/src/2021-11-11-captain-tttm-7.jpg\\" alt=\\"imagee\\" /></p>\\n\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的阿里一年香(入职阿里一周年)</title>\\n \\t<meta name=\\"description\\" content=\\"本文记录了麦克船长来到阿里巴巴集团整整一年时,麦克船长的主管给的寄语。考虑到公司商业敏感问题,做了一定的删节。现记录于此,用于以后的回顾。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的阿里一年香(入职阿里一周年)</h2>\\t\\t\\n\\t<time datetime=\\"2021-06-04T15:42:43+00:00\\" class=\\"by-line\\">04 Jun 2021, 杭州 | 作者 麦克船长 | 总计 304 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>To 钟超</p>\\n\\n<p>1 周年快乐!很开心我们有这样一段共事的机会,虽开始时有些许波折,但随着进一步相处,我们很快能做到彼此欣赏、英雄相惜、默契配合,也特别感谢你对我的信任和支持,这是一切共事的基础。你强大的自驱力、脑力、对新事物的理解学习能力,都是最近几手新人里比较突出的。特别钦佩于你的执着和初性,对一件事认定后,迸发出的强大战斗力和决心。今天特卖这个新业务需要扎下根基,还真的需要一些舍我其谁的胆魄和更为犀利的突破,我也相信「新特卖」能成为你在阿里又一代表作,我希望我们的团队能为之骄傲和自豪,我们能不负公司所托,真正在下沉市场这场硬仗上有所建树,井取得令我们自己感到骄傲的突破,一起加油。</p>\\n\\n<p>From 麦克船长的主管</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>担任淘宝产品总负责人的双十一,是怎样的体验?</title>\\n \\t<meta name=\\"description\\" content=\\"本文记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>担任淘宝产品总负责人的双十一,是怎样的体验?</h2>\\t\\t\\n\\t<time datetime=\\"2020-11-11T15:59:43+00:00\\" class=\\"by-line\\">11 Nov 2020, 杭州 | 作者 麦克船长 | 总计 138 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>说是体验,其实本文只记录了一些影像,是关于麦克船长来到阿里巴巴集团的第一个双十一,负责担任淘宝的总PD(产品总负责人)。一年一度的双十一成了淘宝,乃至整个阿里集团的传统,就像阿里这家公司的春节过年一样,气氛热烈,而且消费者和商家朋友们也都会跟我们一同迎来一次购物与销售的狂欢。</p>\\n\\n<p><img src=\\"/img/src/2020-11-11-captain-double-eleven-1.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-2.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-3.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-4.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-5.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-6.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-7.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-8.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-9.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-10.jpg\\" alt=\\"image\\" />\\n<img src=\\"/img/src/2020-11-11-captain-double-eleven-11.jpg\\" alt=\\"image\\" /></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>疫后怎么做餐饮品牌?三叉戟模式或成标配</title>\\n \\t<meta name=\\"description\\" content=\\"2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>疫后怎么做餐饮品牌?三叉戟模式或成标配</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-14T16:42:43+00:00\\" class=\\"by-line\\">14 Apr 2020, 杭州 | 作者 麦克船长 | 总计 1845 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2020-04-15-covid2019-catering-business-mode-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>2020 新型冠状病毒疫情,给所有商业领域都带来了巨大影响,而餐饮业可以说是首当其冲,但这同时也带来了很多多元化经营的启示。</p>\\n\\n<p>我们回归原点,餐饮业解决了我们什么需求?吃饭。但是当我们不选择去饭店就餐时,我们如何解决吃饭问题?</p>\\n\\n<ul>\\n <li>外卖</li>\\n <li>做饭</li>\\n <li>速食</li>\\n</ul>\\n\\n<p>而这三方面,恰恰就是餐饮企业多元化经营的答案。</p>\\n\\n<h3 id=\\"标品化外卖业务\\">标品化外卖业务</h3>\\n\\n<p>一些以堂食为主的大餐饮品牌,比如海底捞、太二酸菜鱼、呷哺呷哺、西贝莜面村等等,应该更加重视外卖的价值了。重视到什么程度?比如这次疫情的影响,让你的成本与收入结构决定你只能关店,那说明你的外卖业务体量仍太小,过度依赖于堂食营收。</p>\\n\\n<p>其实大品牌做外卖,具有先天优势:信任度、定价优势。</p>\\n\\n<p>将门店部分菜品做标准化,设定外卖菜单,将外卖业务作为门店的重要多元化经营手段之一:</p>\\n\\n<ul>\\n <li>形成<strong>场景互补</strong>,可以<strong>增强抗风险能力</strong>,除了这次的瘟疫,其他很多情况都会导致消费者外储减少,进而出现区域性的门店营收下降,比如台风、雾霾、暴雨等等。</li>\\n <li><strong>增加品牌露出渠道</strong>。门店模式,以线下地段的人流曝光、点评等「到店」为主的互联网平台曝光为主,而外卖可以带来「到家」为主的互联网平台曝光。</li>\\n</ul>\\n\\n<h3 id=\\"社区生鲜前置仓\\">社区生鲜前置仓</h3>\\n\\n<p>数据显示,京东生鲜配送到家业务相对节前环比增长 370%,叮咚买菜大年三十的订单量同比上月增长超过 300%;美团买菜在北京地区的日订单量达到了春节节前单量的 2-3 倍;除夕至初四,每日优鲜平台实收交易额较去年同期增长 321%。</p>\\n\\n<p>而餐饮门店,先天性地就需要大量采购生鲜食材、调味品,而采购量如果不合理,还会出现库存积压甚至损失的问题。如果餐饮连锁品牌把每家门店本身,变为一个生鲜食材的社区前置仓,反而比生鲜电商更具有优势。</p>\\n\\n<p>从另一个角度说,叮咚买菜、美菜等生鲜电商平台,甚至美团,也可以寻求和某个或某几个门店数量较多、分布较匹配的餐饮连锁品牌合作,对于自己的市场扩张也是很大的助力,是一种双赢。</p>\\n\\n<p>这样看来,盒马鲜生最初尝试的「超市+堂食+生鲜配送」的模式,或成为最佳先行者案例。以购物为主业的物美、永辉、联华,其实都可以进化成这种模式,而以堂食为主业的餐饮门店,可以用更社区的方式,进化成这种模式。</p>\\n\\n<p>在这方面,餐饮企业应该发挥自身优势,避开短板。购物业态发展起来的生鲜配送,往往只能提供蔬菜禽蛋肉,而餐饮企业除此之外,还可以提供半成品食材,方便消费者进行简餐烹饪就可做出一道菜。</p>\\n\\n<p>总结下餐饮做社区生鲜前置仓的特点:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li>培养消费者习惯,<strong>加深品牌认知</strong></li>\\n <li><strong>加速库存周转</strong>,提升采购弹性</li>\\n</ul>\\n\\n<h3 id=\\"线上预包装食品\\">线上预包装食品</h3>\\n\\n<p>从本次疫情的速食类预包装食品销售大幅增长来看,当人们无法外出就餐,也不想自己生火做饭时,速食预包装食品依然是最重要的就餐保底选择。</p>\\n\\n<p>大餐饮品牌非常适合拓展预包装食品,而且消费者认知里会觉得大餐饮品牌的预包装食品更有品质、更安全。这样就需要品牌选好关联品类,比如川菜、湘菜品牌,推出辣味食品就很符合消费者心智认知;新疆、内蒙的地方特色餐饮品牌,则可以提供牛羊肉类的预包装零食;海鲜类餐饮品牌,可以推出水产类零食,等等。</p>\\n\\n<p>提供预包装食品,会从四方面助力餐饮品牌发展:</p>\\n\\n<ul>\\n <li><strong>场景互补,增加收入模式,提升抗风险能力</strong></li>\\n <li><strong>更多渠道触达</strong>,原来传统餐饮品牌,在互联网领域最多触达到到店消费和外卖两个场景。增加预包装食品后,可以在众多电商平台曝光,并且进一步的增加抖音、快手、小红书、淘宝直播等自媒体种草与带货渠道,还可以在有赞、微盟支持先与公众号流量主合作。更进一步的还有社交电商、微商体系。</li>\\n <li><strong>突破门店区域触达限制</strong>,对于预包装食品,只要快递能到达的范围,都是自己的客户覆盖区域。</li>\\n <li>加深消费者认知,可以在一日三餐之外,有更多的场景唤起消费者。</li>\\n</ul>\\n\\n<h3 id=\\"线下重构新餐饮时代到来\\">线下重构,新餐饮时代到来</h3>\\n\\n<p>危机也是发展的契机,这一次疫情必然带来线下全面的线下格局洗牌。</p>\\n\\n<ul>\\n <li>用「标品化外卖」覆盖外卖场景</li>\\n <li>用「生鲜前置仓」覆盖做饭场景</li>\\n <li>用「预包装食品」覆盖速食场景</li>\\n</ul>\\n\\n<p>而餐厅核心能力,为这三方面做供给支撑,这就是我们说的「<strong>三叉戟模式</strong>」。受翻台率限制的堂食则作为一个可选项,对客单价偏高的餐饮品牌,堂食依然占据重要意义,而低客单价的餐饮品牌或许三只尖刺的杀伤力会远超主柄。</p>\\n\\n<p>对于餐饮品牌来说,未来运用三叉戟模式,可能会成为一种常态。率先做出这种布局调整的餐饮品牌,会在线下流量重构的过程中,成为新餐饮时代的代表。</p>\\n\\n<p>最后还是想说一句,希望疫情早些结束,希望中国的餐饮企业们沉着应对,降成本、转模式都要趁早。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>延迟满足,才有自由</title>\\n \\t<meta name=\\"description\\" content=\\"今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>延迟满足,才有自由</h2>\\t\\t\\n\\t<time datetime=\\"2020-04-11T06:18:03+00:00\\" class=\\"by-line\\">11 Apr 2020, 杭州 | 作者 麦克船长 | 总计 4478 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>今天我们来聊聊延迟满足(Delayed Gratification)和即时满足(Instant Gratification)。</p>\\n\\n<h3 id=\\"1儿童时期的延迟满足\\">1、儿童时期的延迟满足</h3>\\n\\n<h4 id=\\"棉花糖实验\\">棉花糖实验</h4>\\n\\n<p>聊到延迟满足,就必须提到沃尔特·米歇尔教授(Walter Mischel)的「棉花糖实验」。在 1960 年时, 米歇尔教授对美国斯坦福大学宾恩幼儿园 600 名 4~6 岁的儿童进行了测试:小朋友们可以选择桌上的棉花糖、奥利奥饼干或椒盐脆饼,然后可以立即吃掉,或者等一会儿再吃。如果等一会儿的话,会得到奖励。</p>\\n\\n<p>研究发现不同的小朋友有不同的表现,平均延迟时间为 15~20 分钟,有些小朋友延迟时间很短,有的则比较长。而 20 多年以后米歇尔教授追踪实验当年的小朋友们,发现延迟时间与学业成功存在正相关性。</p>\\n\\n<p>而今天我想更多地从我自身的感受来看,在不同方面的延迟满足或即时满足,可能是幼儿时期就有的,也可以是成年后习得或改变的。但无论何时,我们都应该对此报以重视。</p>\\n\\n<p>先讲两个我自己的例子吧。</p>\\n\\n<h4 id=\\"黑加仑零食\\">黑加仑零食</h4>\\n\\n<p>我有一个哥哥。在我还没有上学的时候,父母每隔一段时间会给我们购买很多零食,买什么也是我们自己决定的。回到家里,我和哥哥会共同决策,给这些零食按照好吃程度排个序。其中没吃过的零食,就靠我们推测。排好之后,倒序来吃,也就是先从好吃程度最低的零食开始。一直印象很深刻,有一个黑加仑口味的零食,在某次排序中,拔得头筹,但是最后发现它并没有想象中好吃。</p>\\n\\n<h4 id=\\"暑假作业\\">暑假作业</h4>\\n\\n<p>另一个故事,也是来自于我和我哥哥,不过不同于上一个例子,这次我们俩有了不一样的表现。</p>\\n\\n<p>记得刚上小学的时候,寒暑假里,我都是先把所有作业做完,再开始玩的。尤其印象深刻的是,炎热的夏天里,我坐着小板凳,在床边写暑假作业,非常有画面感。而忘记是从哪年开始,哥哥在假期开始后,带着我先玩,我才跟他学会了「原来还可以先玩」。</p>\\n\\n<h4 id=\\"延迟满足的背后是意志力自控力\\">延迟满足的背后,是意志力/自控力</h4>\\n\\n<p>「黑加仑零食」和「暑假作业」都是我很小的时候,延迟满足的例子,而且基本来自于主动选择延迟满足。</p>\\n\\n<p>从相对没有那么好吃的零食开始,一直吃到最好吃的零食,整个过程的体验,是越来越美妙,充满期待的。而如果反过来,则会对后面的零食,越来越没有兴趣。最后结果就是某些零食,一直到过了保质期,也不会被吃掉。</p>\\n\\n<p>暑假里如果先玩个痛快,最后赶作业,其实前面玩的就不够放松。而如果先把作业做完,就可以彻彻底底地疯到开学,100% 的放松状态。</p>\\n\\n<p>如果考虑到婴幼儿时期的家庭教育,或有影响,那么也不能说延迟满足就是可以先天具备的。但是先天还是后天并不是我们讨论的重点,本文也不是学术论文。</p>\\n\\n<p>延迟满足的根本,其实是自控力/意志力。和前面提到的米歇尔教授一样,另一位来自斯坦福大学的心理学家凯利·麦格尼格尔(Kelly McGonigal),她有一本书叫《意志力》,也有译作《自控力》。按我的理解,自控力就像重力势能,从自控力很低的状态想爬坡到高自控力状态,需要克服很多,而反过来却轻而易举。</p>\\n\\n<p>因此对于自控力,需要长期的刻意练习形成,并且不能轻易打破。只有在高自控力下,才能形成对满足感的延迟有很强的掌控能力。</p>\\n\\n<p>那么问个更根本的问题:延迟满足,确定是好于即时满足吗?</p>\\n\\n<h3 id=\\"2快乐理论挑战延迟满足\\">2、快乐理论,挑战延迟满足</h3>\\n\\n<p>记得中学时,听老师讲过一个关于「烂苹果」的小故事,给我的延迟满足习惯带来了极大的挑战。</p>\\n\\n<h4 id=\\"烂苹果\\">烂苹果</h4>\\n\\n<p>有一个中国老太太,和一个某国老太太(忘记是哪国了),各买了一箱苹果。一开始都有个别几个,没那么新鲜,有一点点要烂了的样子。中国老太太比较会过日子,都是先挑烂的吃。而外国老太太会享受,先挑好的吃。</p>\\n\\n<p>于是,中国老太太几乎每次都在吃烂苹果,因为随着时间的推移,原来新鲜的也开始变烂了。而外国老太太,先把好苹果吃完了,那些一点点烂的苹果,后来就烂得很严重,干脆就扔了。</p>\\n\\n<p>结果就是,中国老太太吃了大量的烂苹果,而外国老太太虽然扔了一些苹果,但她吃掉的都是好苹果。由此引申说中国人在很多事情上都是在吃烂苹果。</p>\\n\\n<p>当时年纪小,对这类很 SB 的瞎编故事,还缺乏足够的反驳意识,尤其是这种中国、外国的夹杂民族自卑感的瞎编故事。但确实给我留下了深刻印象,以至于我会去想:是不是我周末回家,可以先打两天红色警戒和扫雷,周日晚上再赶作业?</p>\\n\\n<h4 id=\\"快乐理论\\">快乐理论</h4>\\n\\n<p>从「烂苹果」的故事里,我们可以引申出一个「快乐理论」。如果用快乐值,来衡量收益,并且认为苹果随着时间推移会逐渐变烂,那么先吃烂苹果的快乐值,始终与烂苹果关联。而先吃好苹果,最后的烂苹果全部扔掉,则快乐值都与好苹果关联。</p>\\n\\n<p>唯一区别是数量,比如可能前者吃了 10 个烂苹果,后者吃了 5 个好苹果。每次平均快乐收益,必然是前者更高。</p>\\n\\n<p>但这个理论角度对吗?</p>\\n\\n<h3 id=\\"3我们应该在哪个范畴内讨论延迟满足\\">3、我们应该在哪个范畴内讨论「延迟满足」?</h3>\\n\\n<p>休闲娱乐,和完成任务,是完全不同的范畴。对于前者来说,其实没什么好讨论的,我们应该关注与后者。</p>\\n\\n<p>完成任务,关联着学习、工作、创业等等。这些是我们人生过程的基石。所以「烂苹果」引出的「快乐理论」,我们没必要去深入讨论对错,起码从「范畴」上来看,就已经错误了。</p>\\n\\n<h4 id=\\"仅有正反馈刺激的奶头乐\\">仅有正反馈刺激的奶头乐</h4>\\n\\n<p>而「完成任务」并不像「吃」那么本能,它需要我们去克服困难、主动思考、建立方法,过程中会遭遇负反馈与正反馈。而吃,在大多数情况下,仅有正反馈。</p>\\n\\n<p>而仅有正反馈的事儿,如果我们持续做它,就会进入奶头乐的陷阱。典型的奶头乐,比如刷短视频、打游戏。因为它们有持续快速的正反馈,没有负反馈,所以它们本身就是「满足」,而我们应该做的是,在大尺度的讨论范畴中学会延迟它们。或者那些混杂正反馈与负反馈的事务,我们要学会延迟那些仅有正反馈的局部。</p>\\n\\n<h4 id=\\"延迟满足重在顺序而非时间\\">延迟满足重在顺序,而非时间</h4>\\n\\n<p>因此,我们讨论到这里就很明显发现,延迟满足的核心是「顺序」而非「时间」。而有的言论甚至愚昧地解读为「拖延」、「不能把握机会」,这就是理解错误了。</p>\\n\\n<p>将仅有正反馈、靠本能即可驱动的事情,优先级排低。而把存在负反馈但又很重要的事情,优先级排高。</p>\\n\\n<h3 id=\\"4常见的延迟满足与即时满足\\">4、常见的延迟满足与即时满足</h3>\\n\\n<h4 id=\\"初阶对手vs浅度延迟满足\\">初阶对手 vs 浅度延迟满足</h4>\\n\\n<p>常见的即时满足,需要我们去延迟的,有这些:</p>\\n\\n<ul>\\n <li>\\n <p>睡懒觉</p>\\n </li>\\n <li>\\n <p>过量饮食</p>\\n </li>\\n <li>\\n <p>玩游戏/刷短视频等手机、PC 娱乐内容</p>\\n </li>\\n <li>\\n <p>冲动情绪</p>\\n </li>\\n <li>\\n <p>炫耀(粗俗说就是装B)</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>这些显而易见属于即时满足的事情,如果我们放着重要事情不做,而先干这些,其实我们会很自然地产生负罪感。所以这些事情只是初阶的延迟满足对手。</p>\\n\\n<p>初阶对手,基本都是完全处于我们本能,来满足我们的及时行乐。</p>\\n\\n<h4 id=\\"中阶对手vs中度延迟满足\\">中阶对手 vs 中度延迟满足</h4>\\n\\n<p>稍高级的对手,是那些并不那么明显的,比如下面这些。</p>\\n\\n<ul>\\n <li>\\n <p>在家边看电视/视频,边学习/工作</p>\\n </li>\\n <li>\\n <p>依赖二手知识,而怠于一手知识</p>\\n </li>\\n <li>\\n <p>刷知乎、行业资讯 APP</p>\\n </li>\\n</ul>\\n\\n<p>……</p>\\n\\n<p>例子会有很多,我们仅稍微说下这三个。</p>\\n\\n<p>我们来看看「在家边看电视/视频,边学习/工作」。你确实在学习/工作,但是你的专注度会极大的降低。而事后你回顾,还很可能认为「我学习了一个下午啊」,其实是你看了一个下午视频,捎带着学习/工作了一下。</p>\\n\\n<p>再来看第二个,什么是「二手知识」呢?就是那些把严重简化、略化的专业知识或内容,比如《10 分钟读懂 XXX》,比如用网红化的视频来讲一个原本需要下功夫的专业内容。看罗振宇《时间的朋友》跨年演讲、参加混沌大学的班级学习、报名朋友圈裂变分享的学习课程…… 这些都是二手知识。二手知识的危害是,你缺乏系统性学习与思考,而把学习和思考给综艺化、娱乐化、浅显化了。</p>\\n\\n<p>刷知乎,会给你一种错觉:你也是精英人群(起码和他们混一个社区的)。刷行业资讯 APP(比如 36 氪、虎嗅这类),也会给你一种错觉:你也是创投达人、专业人士(起码是跟他们混在同一个平台的)。这些错觉,会让你对自我身份认知出现偏差,获得一种虚无的满足感。</p>\\n\\n<h4 id=\\"高阶对手vs深度延迟满足\\">高阶对手 vs 深度延迟满足</h4>\\n\\n<p>有些延迟满足,是更深层、更高阶的。面对的对手,也是更高阶的。</p>\\n\\n<p>高阶对手的特点,是伪装成很正确的样子,实则对于已经进阶到高阶排位赛的你来说,会是更危险的敌人。</p>\\n\\n<p>我这里就说一个影响很大的:</p>\\n\\n<p>* 急于行动</p>\\n\\n<p>其实当代年轻人,勤奋程度大多没有问题。但是过于勤奋,可能会形成急于行动的坏习惯。</p>\\n\\n<p>对于执行力差的人,「先干再说」、「Just Do It」是很好的激励口号。但是对于执行力已经很优秀的人,「三思而后行」反而成了更重要的事。</p>\\n\\n<p>对于行动力强的人来说,行动本身甚至可以给他带来快感。而这快感,恰恰就是一种「正反馈的满足感」,如果形成了对这种满足感的过度依赖,那么「行动」本身,就成了我们应该着力去延迟的满足,以避免思考上的「武断」,或行动上的「鲁莽」。</p>\\n\\n<p>我们常听到的「战术的勤奋掩盖战略的懒惰」、「纸上得来终觉浅,绝知此事要躬行」,这些激励式的口号,都容易让我们变为「急于行动」的暴躁老哥。</p>\\n\\n<p>但是这两者也要注意平衡,如果矫枉过正,又会变成「过于纸上谈兵」的人。</p>\\n\\n<h3 id=\\"5延迟满足的驻留时长\\">5、延迟满足的驻留时长</h3>\\n\\n<p>延迟满足,并不是一个「是否」的选择问题,而是一个需量化衡量的能力指标。延迟满足能力的量化,就是延迟的驻留时长。</p>\\n\\n<p>你在延迟驻留多久以后,就需要给予正反馈满足了?比如我们常看到「你再写完一页,我就让你玩 10 分钟」这样的家长教育小朋友的场景。这里「一页」就意味着一个驻留时长。</p>\\n\\n<p>对于延迟满足能力强的人,也就是驻留时间非常长的人,他可能把全部工作全做完,甚至还能反复修改检查、打补丁、升级,再进入到正反馈满足阶段(休息或做其他事情)。而驻留时间短的人,可能做了 3 个小时,完成了一部分,他就要休息一下,娱乐一下。</p>\\n\\n<p>这是表层的差别,更深层的差别在于心理层面。比如有的人对于情绪释放这种事儿,我曾经认识一个 19 岁当上大酒店的大堂经理的人,老板对其他员工宣传他 23 岁,是为了帮助他树立威信。而这个大堂经理,每隔一段时间就会到老板办公室来,跟老板发泄做情绪释放,而其他员工看到的,一直是一个没有情绪化的大堂经理。</p>\\n\\n<p>这里面就体现出了延迟满足的驻留时间,以及驻留结束后的满足获得(情绪释放)。我们很难做到无限长,所以选择合理的满足获得方式,很重要。</p>\\n\\n<p>而有的人,则能完全化解于无形,无论在商业谈判中的挑衅,还是下属的忤逆犯上,还是陌生人的出言不逊,他都能很好的自控、消解,这种人的驻留时间,某种意义上已经达到无限长了,也就是在某件事儿的延迟满足上已经做到完全内化了。</p>\\n\\n<p>其他方面也与情绪控制类似,能将长驻留的延迟满足内化为习性,对自由就会有更大的掌控。自由是什么呢?就是我们常听到的那句「自由不是你想做什么就做什么,而是你不想做什么就不做什么」。而不想做什么就不做,除了面对外部的选择权,更多是面对自己的,而这恰恰就是长驻留的延迟满足。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>总结一下,面对不同的「对手」,我们要做到不同深度的延迟满足。而延迟满足的驻留时间,则量化了我们在相应深度上的延迟满足能力。有意培养,刻意练习,用延迟满足来帮助自我成长,是一个长期课题,我也在路上。</p>\\n\\n<p>对于延迟满足的实操,如果日后我有更多梳理,会再行文。也期待与朋友们的讨论,欢迎添加我的个人微信号:sinosuperman 。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>未来人工智能就是要:让普通人过上现在富豪们的生活</title>\\n \\t<meta name=\\"description\\" content=\\"有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>未来人工智能就是要:让普通人过上现在富豪们的生活</h2>\\t\\t\\n\\t<time datetime=\\"2017-02-23T18:23:33+00:00\\" class=\\"by-line\\">23 Feb 2017, 北京 | 作者 麦克船长 | 总计 627 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>如果我很有钱,我就会雇佣一名私人旅行助理,帮我制定旅行计划,购买叫票,预定酒店,打包行李。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人造型师,帮我购买服饰鞋帽,安排各项活动应该的着装。</p>\\n\\n<p>如果我很有钱,我就会雇佣一名私人医生,关注我的健康状态,处理和解答一切日常医护问题。</p>\\n\\n<p>类似的还有私人律师,私人营养师,私人教师……</p>\\n\\n<h4 id=\\"但是我没有钱呢\\">但是我没有钱呢?</h4>\\n\\n<p>有很多领域,需要专业人士面对具体的问题,给出个性化的解决方案。想获取这些个性化的解决方案,就要用金钱作为交换代价。</p>\\n\\n<p>而人工智能(Artificial Intelligence)真正能够发挥巨大作用的,恰恰就是这些领域。</p>\\n\\n<p>技术的边际成本趋于零,使得私人旅行助理、私人造型师、私人医生、私人律师、私人营养师、私人教师可以低成本、高效率地解决这些问题。</p>\\n\\n<p>这需要云端<strong>计算能力</strong>的支持,<strong>海量数据</strong>的支撑,<strong>算法模型</strong>的发展,和<strong>产品设计</strong>上的场景化。目前来看,旅游是较早发力做 AI 旅行定制的,其他领域也都在探索。</p>\\n\\n<p>AI 最先颠覆掉的,就是这些领域里的低级工种。比如律师事务所里负责文书整理工作的小律师、医院里负责病例整理的小护士… 这些工作因其特别符合计算机数据处理的口味,只要信息实现比特化、计算能力足够强,就能很高效的解决。</p>\\n\\n<p>更进一步,当更多维度的数据被完善,更人性化、场景化的产品细节被考虑到后,这些领域被 AI 提升生产力的现象会更进一步渗透。</p>\\n\\n<p>未来,将会有越来越多的工作不再需要人力去完成,现在我们可以先看看富豪们都雇了哪些助理,那都是科技行业工作者的机会。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>我们是应该「断舍离」还是「念念不忘,必有回响」</title>\\n \\t<meta name=\\"description\\" content=\\"如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>我们是应该「断舍离」还是「念念不忘,必有回响」</h2>\\t\\t\\n\\t<time datetime=\\"2017-01-31T20:59:21+00:00\\" class=\\"by-line\\">31 Jan 2017, 北京 | 作者 麦克船长 | 总计 2577 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"引子\\">引子</h3>\\n\\n<p>我们夸夸其谈着自己的风光往事,好不热闹。隔壁的老王也在,他一手扶在桌上,一手在胸前比划,上嘴唇一碰下嘴唇,段子信手拈来,逗得我们捧腹不已,笑声绕廊。老韩也开始讲起了衡水的传说,每次都不重样儿。猛兄靠在门框上,笑得眼镜在鼻梁上乱颤。</p>\\n\\n<p>宿舍里,只有来自黄冈的老朱一人,默不作声,不时看两眼手中的书,不时又看着正在说话的人,被逗得也跟着笑起来。</p>\\n\\n<p>「嗨,我说老朱,讲讲你啊!」</p>\\n\\n<p>「我就不提啦。」</p>\\n\\n<p>「为什么啊?说说,说说!」</p>\\n\\n<p>「我不太想提高中这些事情。你们聊,我先出去一下哈。」</p>\\n\\n<p>「哎,真是的,行行行,你去吧…… 刚才咱们说到哪了?」</p>\\n\\n<p>那是十年前,我和我的大学同学们刚刚入学不久,在宿舍里一起回忆着各自高中的趣事,吹着高考高分的牛逼满天飞的日子。</p>\\n\\n<p>毕业后,老韩去中科院的北京某研究所读书,老王去了佐治亚(GalTech)读博士,猛兄去了伯克利(Berkeley)。老朱是我们这些人里最有出息的,他去了斯坦福(Stanford),研究火箭和空气动力学什么的。而我去了后来成为纳斯达克上市公司的华南某互联网公司。</p>\\n\\n<p>每个大学入学之初似乎都是这样,大家都是对高中那些事儿念念不忘,对高考的得与失记忆犹新。在多次聊天中,老朱对这类话题都打了哈哈,我就开始有些好奇。某次和老朱单独相处,聊了很久,聊开了后我问起了此事,老朱带着那么一点假正经的样子对我说:</p>\\n\\n<blockquote>\\n <p>我给自己订了规矩,叫「不提三高」:高中、高考、高分。</p>\\n</blockquote>\\n\\n<p>为什么?我想在读这篇文章的你,从标题可能猜到了一二。老朱解释了原因,用后来流行的话概括说,就是「<strong>断舍离</strong>」。</p>\\n\\n<h3 id=\\"断舍离\\">断舍离</h3>\\n\\n<p>「断舍离」一词,出自于日本作家山下英子所著的同名书籍《断 · 舍 · 离》,在此书出版后,这一词开始流行起来,那大概是在 2013 年。这词本意是指一种实操性很强的整理术。而整理术,则是带有浓重日本文化色彩的一种关于生活物品整理的方法论。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/725f80ab-4efc-4b06-8eeb-f29fe6afe50e.webp\\" alt=\\"image\\" /></p>\\n\\n<p>进而,这种理念得以传播后,便超越了整理术的应用范畴,开始影响一些我们生活的其他方面。</p>\\n\\n<p>无论是老朱的「不提三高」,还是山下英子的「断舍离」,其隐含的本质都是,不要沉溺于过去。言谈,图一时口快,却把你带回过去,更囿于成败得失,而弱化了未来的规划和执行力。实物,载过往回忆,但使你常念旧日,且占据生活留白,则减少了放空的机会和轻松感。</p>\\n\\n<h3 id=\\"念念不忘必有回响\\">念念不忘,必有回响</h3>\\n\\n<p>与「断舍离」一词同样流行于 2013 年前后的,还有一句话,叫「念念不忘,必有回响」。这一句最早出自弘一法师的《晚晴集》。</p>\\n\\n<p><img src=\\"https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ya2QnV41Kod8O4XB/img/494aa186-be1b-4ee1-a2e2-8704c8434e50.webp\\" alt=\\"image\\" /></p>\\n\\n<p>2013 年,王家卫导演的电影《一代宗师》中引用了这句话而使其广为传播。</p>\\n\\n<p>值得我们「念念不忘」的,必是让我们在未来的生活中更能感到或力量、或幸福、或希望的憧憬或回忆。历史能够成书万卷,都是来自我们一代代人对过去的念念不忘,无论是坚硬的国仇家恨,还是柔软的儿女情长;科技能够加速更迭,也都来自于人类天性的好奇心驱使,不断探究已知问题的未知边界。</p>\\n\\n<p>但我们有时会听到不同的声音:「不要活在过去」、「不要太执着」、「学会归零」、「学会放下」…… 还有列宁老师提醒我们「忘记过去,就意味着背叛」。</p>\\n\\n<p>那我们什么时候应该「断舍离」,什么时候又应该「念念不忘」?有没有什么具备实操价值的方法?</p>\\n\\n<h3 id=\\"用正负反馈来判断何时何为\\">用「正负反馈」来判断何时何为?</h3>\\n\\n<p>一个简单的办法,就是用「正负反馈」来判断。<strong>如果对某事、某人、某物的执念,会对我们的人生产生负反馈,我们就应该对此事、此人、此物「断舍离」;相反,如果是正反馈,则应该「念念不忘」。</strong></p>\\n\\n<p>「断舍离」最好的例子,一定是整理术。「扔东西」会给你带来极大的快感。克制自己的购物欲,不仅省钱,也会省空间,毕竟生活于现代社会的我们,空间是何其的有限。我们辛辛苦苦赚来一平方米要好几万块的房子,当然不是为了堆放那些「将来总会用到」而其实根本不会用到的废物的。</p>\\n\\n<p>生命总被分成不同的旅程:中学到大学,大学到社会,国内到国外,工作到跳槽,合租到独居,单身到结婚,二人世界到家庭生活,一线员工到部门领导 …… 每次不同的人生状态跨越后,我们都会调整自己的生活节奏和作息规律,我们也更应该调整自己的心理状态。</p>\\n\\n<p>荣耀加身时,对我们的激励已经得到最大化,不必担心你从那次成绩中吸取的信心还没有被完全消化,而要担心它在你的心中风头过劲,以至于令你慢下脚步。</p>\\n\\n<p><strong>能够取得连续成功的人,往往都懂得「断舍离」</strong>。</p>\\n\\n<p>Yin 是我曾经的一位合伙人,在中学时获得了「国际数学奥林匹克竞赛金牌」,后来又取得美国一所顶尖大学的博士学位,并在多年后拿到很多华人留学生羡慕的美国某大学终身教职(tenure)。Yin 对探索高深技术问题,有着天生的狂热,类似这样的人我还认识不少。他们并不见得都谦虚谨慎,有得也是狂放不已,但都会保持较高的自律,很少放任自己。</p>\\n\\n<p>在进入新的学校后,忘掉自己曾是「全校第一」;在来到新的公司后,忘掉自己是「核心骨干」。如果这时「念念不忘」,听到的回响,肯定不是什么锣鼓喧天,也不会是鞭炮齐鸣,而不过是自己的一个屁。</p>\\n\\n<p>再过一年半,将是我的一位至亲离开我二十年整的日子。坦率地说,我不知道在十二岁时经受这样的打击,与在三十二岁、四十二岁时经历,有何区别,尽管我希望这来的越晚越好。但那种悲痛,真的会在几年后转变为长久的生活力量。我们的每个亲友皆有生老病死,我想很多读者朋友会明白这种转变过程。而这转变前后,我们应该怎样调节自己呢?</p>\\n\\n<p>倘若亲人的离世,对你打击很大,此时刻意的忘却是很难的,相反在一段时间内你都需要进行积极的心理建设。但那段时间过后,你应该「断舍离」还是「念念不忘」?我相信大多数成年人能够处理好,一般我们会把这种「想念」转化为生活的动力,他们的离去会让我们懂得更多,比如珍惜,比如完成遗志。这都是对未来的「念念不忘,必有回响」,这也便是对生活的正反馈。</p>\\n\\n<p>所谓「化悲痛为力量」,具体操作起来的办法,则是在<strong>痛苦初期尽量「断舍离」,尽量转移注意,尽量寻找排解出口</strong>,无论这种痛苦是来自工作、家人,还是学业、朋友。而<strong>后期开始,化为力量,才能「必有回响」</strong>。</p>\\n\\n<h3 id=\\"后记\\">后记</h3>\\n\\n<p>此篇的成文之日是 2017 年 2 月 1 日,农历丁酉年正月初五。在新的一年里,我也要对过往做个整理,对未来做个规划。而读完本文,不论你身处何时何地,也可以在心中整理一下,看看对哪些事、哪些人、哪些物应该「断舍离」了,而又对哪些事、哪些人、哪些物应该「念念不忘」。</p>\\n\\n<p>然后,我们一起等待,那声回响。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>一名出色软件工程师的技术基本功:编程与工具</title>\\n \\t<meta name=\\"description\\" content=\\"再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>一名出色软件工程师的技术基本功:编程与工具</h2>\\t\\t\\n\\t<time datetime=\\"2012-05-15T17:06:59+00:00\\" class=\\"by-line\\">15 May 2012, 广州 | 作者 麦克船长 | 总计 2132 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"0写在前面\\">0、写在前面</h3>\\n\\n<p>再过一个多月,我就毕业工作一年了。目前在广州的 YY 语音,是 Web YY 音视频媒体技术负责人,公司预计在下半年上市,我希望通过 Web 版 YY 能为用户更容易访问(免注册、免登陆)来拉动 YY 的 DAU(活跃用户人数)助力 YY 上市。夜深人静,写一些自己对于出色软件工程师技术基本功的理解。</p>\\n\\n<h3 id=\\"1编程\\">1、编程</h3>\\n\\n<p>首先至少精通一门高级语言(注意是精通),然后要熟悉额外的几门语言。举例来说:</p>\\n\\n<h4 id=\\"如果你精通c语言\\">如果你精通 C 语言</h4>\\n\\n<p>那么除了其语言标准之外,还要精通 Linux 平台的系统 API,以及一些常用的库,还有单元测试工具。当然,如果你需要精通 C 语言的话,应该是需要你经常做与操作系统直接接触的应用底层开发,或者编写一些基础库。</p>\\n\\n<h4 id=\\"如果你精通c语言-1\\">如果你精通 C++ 语言</h4>\\n\\n<p>那么除了 C++ 语言标准外,你应该还要精通 STL(虽然这已经纳入 C++ 标准,但是我还是要提两句),以及一些常用的库,比如 Boost、ACE、POCO 等。</p>\\n\\n<p>另外,精通 C/C++ 要求你必须要会用 GCC/G++、GDB、Makefile(整合 Makefile 的 CMake 等)/Scons 等等。</p>\\n\\n<h4 id=\\"精通的关键还是针对语言核心来说的\\">精通的关键,还是针对语言核心来说的。</h4>\\n\\n<p>第一,你要对这个语言的语法特性熟稔;</p>\\n\\n<p>第二,你要对这个语言的标准库的每个 API 熟稔;</p>\\n\\n<p>第三,你要能够熟练运用这门语言编写各种设计模式;</p>\\n\\n<p>第四,你能够运用你对这门语言的掌握,完成任意给定的编程任务。</p>\\n\\n<p>那么,其他额外要熟悉的语言,你要做到有的放矢,就是当你要进行某种开发的时候,你在这方面能够熟练使用这门语言。比如你可以用 PHP 熟练地进行 Web 开发,你可以用 Perl 熟练地处理文本,你可以用 Bash 熟练地编写脚本小工具。</p>\\n\\n<h4 id=\\"与计算机网络的基础结构相关联的技术实现\\">与计算机、网络的基础结构相关联的技术实现</h4>\\n\\n<p>除了这些呢,设计模式、异步 IO、进程与线程、网络编程也是你必须精通的。当然,你只要精通你所使用的语言的这些方面的就可以了。</p>\\n\\n<h3 id=\\"2工具\\">2、工具</h3>\\n\\n<p>对于工具有三个层面:</p>\\n\\n<p>第一,是熟练的使用一些工具。</p>\\n\\n<p>第二,是能够发现提高生产力的工具。</p>\\n\\n<p>第三,是能够在无可用工具时自己编写工具。</p>\\n\\n<p>那么都有哪些最最最基本的工具呢?</p>\\n\\n<h4 id=\\"ideintegrateddevelopmentenvironment\\">IDE(Integrated Development Environment)</h4>\\n\\n<p>第一自然是 IDE,这是程序员的武器。如果你是 Windows 下的 C/C++ 开发者,建议你使用 Visual Studio,不要小看它,如果你能够精通它,你也算是一个高手。如果你是 Mac 下的C/C++/Objective-C 开发者,可以选择 XCode、Eclipse,并配合 Vim/Emacs 使用。如果你是 Linux 下的开发者,可以使用 Vim/Emacs。</p>\\n\\n<h4 id=\\"vcsversioncontrolsystem\\">VCS(Version Control System)</h4>\\n\\n<p>VCS 可以分为两类,一类是 CVCS(Central VCS),另一类是 DVCS(Distributed VCS)。现在 CVCS 一般使用 SVN、CVS,DVCS 一般使用 Git、Mercurial(Hg)。至于 CVCS 和 DVCS 的区别,道地谁先进,我喜欢下面这段比喻:</p>\\n\\n<blockquote>\\n <p>Once you understand the conceptual differences between CVS/SVN and Git, and then subsequently start to use Git, you may find it very difficult to go back. You should really start to experiment only if you think you’re going to migrate in the near future, because using Git is like watching TV in colour: once you’ve discovered it, it’s really difficult to go back to black &amp; white.</p>\\n</blockquote>\\n\\n<p>一旦你使用了 VCS,你就会接触到 Google Code、Github、BitBucket 等等。它们其实可以算是一种在线工具。</p>\\n\\n<h4 id=\\"clicommandlineinterface\\">CLI(Command Line Interface)</h4>\\n\\n<p>我们一般都说命令行(Command Line),为什么还带一个「I」呢?类比 API(Application Program Interface)、GUI(Graphical User Interface)就能明白了,这都是与某个系统的交互接口,API 是通过一些 Library 调用实现交互,GUI 是通过在图形界面上的点击/拖动/滑动等实现交互。熟练地运用操作系统的 CLI。无论你是使用 Linux、Mac、Solaris、FreeBSD,甚至是 Windows,你都要熟练使用 CLI。</p>\\n\\n<h3 id=\\"3结语\\">3、结语</h3>\\n\\n<p>还能想到什么?由于现在夜深人静,头脑不够清醒,只能想到这些。况且在这些方面,我也达不到「精通」,甚至想去甚远。那姑且先这样吧,如果哪位朋友有什么想说的,可以在下面给我留言,我会补充到文中。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"web":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>麦克船长的 Jekyll 快速教程</title>\\n \\t<meta name=\\"description\\" content=\\"Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>麦克船长的 Jekyll 快速教程</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-23T19:43:02+00:00\\" class=\\"by-line\\">23 Dec 2021, 杭州 | 作者 麦克船长 | 总计 3903 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>Jekyll 是一个用 Ruby 实现的、使用 Liquid 模板引擎的静态网站生成器,它可以通过 Markdown 或者 HTML 等文件生成完整的静态网站。它特别适用于博客或者文章类的网站,因为可以自动生成博客的首页、分类页、标签页等等。因为使用 Liquid 引擎所以能在页面中使用变量、循环、条件语句等等,非常方便。虽然基于 Ruby 实现但使用起来并不需要掌握 Ruby,只需要了解一些基本的语法即可。</p>\\n\\n<h3 id=\\"part-1基本特点\\">Part 1、基本特点</h3>\\n\\n<h4 id=\\"一基本语法\\">一、基本语法</h4>\\n\\n<ul>\\n <li>变量:用双大括号表示变量 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code></li>\\n <li>过滤器:可以使用过滤器对变量进行操作,例如 <code class=\\"language-plaintext highlighter-rouge\\">麦克船长的技术、产品与商业博客</code> 表示把网站的标题转换为大写。</li>\\n <li>支持循环与分支结构:比如 <code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">if-elsif-else-endif</code> :可以使用 <code class=\\"language-plaintext highlighter-rouge\\">fo-endfor</code> 循环遍历列表或集合,例如 `````` 表示遍历网站的所有页面。</li>\\n</ul>\\n\\n<h4 id=\\"二典型-jekyll-项目结构及重要文件介绍\\">二、典型 Jekyll 项目结构及重要文件介绍</h4>\\n\\n<h5 id=\\"1配置文件-_configyml\\">1、配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code></h5>\\n\\n<p>首先看到下作为一个网站的基础设置,这里要特别注意不要遗漏 <code class=\\"language-plaintext highlighter-rouge\\">encoding: utf-8</code> 这一条。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Site settings</span>\\n<span class=\\"na\\">encoding</span><span class=\\"pi\\">:</span> <span class=\\"s\\">utf-8</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">麦克船长的技术、产品与商业博客</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">麦克船长对于技术、产品、商业等领域的分享|AI,A.I.,NLP,神经网络,人工智能,自然语言处理,BERT,GPT,ChatGPT,OpenAI,阿里巴巴,P9,运营,淘宝,天猫,总监,高管\\"</span>\\n<span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptain.com\\"</span>\\n<span class=\\"na\\">author</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">name</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Your</span><span class=\\"nv\\"> </span><span class=\\"s\\">Name\\"</span>\\n <span class=\\"na\\">url</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">https://www.mikecaptian.com\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后是 Markdown 引擎的设置,及其高亮语法 Rouge 部分。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Markdown and highlighter</span>\\n<span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>一些要用到的插件也要设置进来,本博客只用到了基础插件两个。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Plugins</span>\\n<span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-paginate</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-sitemap</span>\\n</code></pre></div></div>\\n\\n<p>另外构建项目的一些关键设置,比如文章放在哪里、如何进行分页(每页多少条文章)等等作为一个静态博客网站的 build 类设置都在此。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\"># Build settings</span>\\n<span class=\\"na\\">baseurl</span><span class=\\"pi\\">:</span> <span class=\\"c1\\"># Change this to your relative path (ex: /blog/), or leave just a /</span>\\n<span class=\\"na\\">source</span><span class=\\"pi\\">:</span> <span class=\\"s\\">.</span>\\n<span class=\\"na\\">destination</span><span class=\\"pi\\">:</span> <span class=\\"s\\">./_site</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:title</span>\\n<span class=\\"na\\">paginate</span><span class=\\"pi\\">:</span> <span class=\\"m\\">20</span>\\n<span class=\\"na\\">paginate_path</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/page:num/</span>\\n<span class=\\"na\\">collections</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">posts</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">output</span><span class=\\"pi\\">:</span> <span class=\\"no\\">true</span>\\n <span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/:year/:month/:day/:title/</span>\\n <span class=\\"na\\">directory</span><span class=\\"pi\\">:</span> <span class=\\"s\\">_posts</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2布局文件_layouts-目录下的文件规则\\">2、布局文件:<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的文件规则</h5>\\n\\n<p>Jekyll 的 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录包含了你的 Jekyll 站点中所使用的页面布局。每个页面布局是一个 <code class=\\"language-plaintext highlighter-rouge\\">HTML</code>模板,定义了你的站点中页面的框架和外观。你可以通过在你的文章或页面的头部添加一个 <code class=\\"language-plaintext highlighter-rouge\\">layout</code> 字段来指定使用哪个布局来渲染该页面。</p>\\n\\n<p>布局文件通常包含用于渲染页面的常见元素,例如头部、尾部和侧边栏。你可以在布局文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">include</code> 语句来插入你的站点的其他文件,例如 header.html 和 footer.html 文件。这样,你就可以在一个地方维护站点的头部和尾部,而不必在每个页面中都进行更新。</p>\\n\\n<h5 id=\\"3页面文件及其头部\\">3、页面文件及其头部</h5>\\n\\n<p>在一个页面的开头,用如下语法表示页面头部:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">page</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/categories/</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">Categories</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>每个页面文件的头部都会有layout,并与 <code class=\\"language-plaintext highlighter-rouge\\">_layouts</code> 目录下的某个文件对应。</p>\\n\\n<h3 id=\\"part-2jekyll-中的全局变量\\">Part 2、Jekyll 中的全局变量</h3>\\n\\n<p>Jekyll 中有许多全局变量可供使用,它们可以在模板中调用。这些变量提供了有关网站,页面,文章和其他内容的信息,可用于在模板中进行条件判断或显示信息。以下是 Jekyll 中常用的一些全局变量:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site</code>:包含有关网站的信息,如网站标题,描述,域名等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">page</code>:包含有关当前页面的信息,如标题,内容,布局等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">post</code>:包含有关当前文章的信息,如标题,作者,日期等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:包含当前页面或文章的内容。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">paginator</code>:包含有关分页的信息,如当前页码,总页数等。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:包含有关网站的所有标签的信息。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">related_posts</code>:包含与当前文章有关的文章的信息。</li>\\n</ul>\\n\\n<p>这些变量可以在模板中使用,比如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;h1&gt;</span>{{ page.title }}<span class=\\"nt\\">&lt;/h1&gt;</span>\\n<span class=\\"nt\\">&lt;p&gt;</span>{{ site.description }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n<span class=\\"nt\\">&lt;ul&gt;</span>\\n {{ for category in site.categories %}\\n <span class=\\"nt\\">&lt;li&gt;</span>{{ category }}<span class=\\"nt\\">&lt;/li&gt;</span>\\n {{ endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span> \\n</code></pre></div></div>\\n\\n<p>Jekyll 还支持自定义全局变量,可以在配置文件 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 中添加任意的键值对,然后就可以在模板文件中使用这些变量了。例如,你可以在配置文件中添加如下内容:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">my_custom_variable</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">Hello</span><span class=\\"nv\\"> </span><span class=\\"s\\">World\\"</span>\\n</code></pre></div></div>\\n\\n<p>然后就可以在模板文件中使用 <code class=\\"language-plaintext highlighter-rouge\\">site.my_custom_variable</code> 访问这个自定义变量了。</p>\\n\\n<h4 id=\\"一site变量\\">一、site变量</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site</code> 的数据结构里包含了所构建的网站的各种基本信息和结构。</p>\\n\\n<h5 id=\\"1sitecategories\\">1、<code class=\\"language-plaintext highlighter-rouge\\">site.categories</code></h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.categories</code> 是一个 array,每个元素取出它的 <code class=\\"language-plaintext highlighter-rouge\\">first</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">category</code> 的名字,如下使用:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>\\n</code></pre></div></div>\\n\\n<h5 id=\\"2sitepages\\">2、site.pages</h5>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 是一个包含所有页面的数组,不仅包括根目录下的页面,还包括所有子目录下的页面。因此,<code class=\\"language-plaintext highlighter-rouge\\">site.pages</code> 中包含的是整个网站中所有的页面。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3其他常用属性\\">3、其他常用属性</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.title</code>:是网站的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.related_posts</code>:相关文章的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.data</code>:从 <code class=\\"language-plaintext highlighter-rouge\\">_data</code> 目录加载的数据。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.static_files</code>:静态文件的列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">site.collections</code>:自定义集合的列表。</li>\\n</ul>\\n\\n<h4 id=\\"二page-变量\\">二、<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量</h4>\\n\\n<p>在 Jekyll 中,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量表示单独页面的数据。它是一个包含多个属性的对象,可以用来存储页面的信息并在模板中使用。一些常见的 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量属性包括:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">layout</code>:表示页面使用的布局模板的名称。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">title</code>:表示页面的标题。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">date</code>:表示页面的发布日期。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">categories</code>:表示页面所属的分类列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">tags</code>:表示页面所属的标签列表。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">content</code>:表示页面的内容(用 Markdown 格式书写)。</li>\\n</ul>\\n\\n<p>在模板中,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.属性名 }}</code> 的方式来访问 <code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量的属性。例如,如果想在模板中输出页面的标题,可以使用 <code class=\\"language-plaintext highlighter-rouge\\">{{ page.title }}</code>。此外,<code class=\\"language-plaintext highlighter-rouge\\">page</code> 变量还有其他属性,如 <code class=\\"language-plaintext highlighter-rouge\\">permalink</code>、<code class=\\"language-plaintext highlighter-rouge\\">excerpt</code>、<code class=\\"language-plaintext highlighter-rouge\\">url</code> 等,可以根据需要调用。</p>\\n\\n<h3 id=\\"part-3控制结构\\">Part 3、控制结构</h3>\\n\\n<h4 id=\\"1if-else-分支结构\\">1、<code class=\\"language-plaintext highlighter-rouge\\">if-else</code> 分支结构</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ if tmp_var == \\"type1\\" %}\\n{{ elsif tmp_var == \\"type2\\" %}\\n{{ elsif tmp_var == \\"type3\\" %}\\n{{ elsif tmp_var == \\"type4\\" %}\\n{{ else tmp_var == \\"type5\\" %}\\n{{ endif %}\\n</code></pre></div></div>\\n\\n<h4 id=\\"2for-endfor-循环结构\\">2、<code class=\\"language-plaintext highlighter-rouge\\">for-endfor</code> 循环结构</h4>\\n\\n<p>不带条件判断的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 循环如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for post in paginator.posts %}\\n\\t<span class=\\"c\\">&lt;!-- Your other sentences --&gt;</span>\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<p>带条件循环的 <code class=\\"language-plaintext highlighter-rouge\\">for</code> 用 Jekyll 里的「过滤器」来实现:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{{ for page in site.pages | where: \\"dir\\", \\"categories\\" %}\\n\\t{{ page.title }}\\n{{ endfor %}\\n</code></pre></div></div>\\n\\n<h5 id=\\"3jekyll-支持的其他结构包括\\">3、Jekyll 支持的其他结构包括:</h5>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">case</code> 用于在多个可能的条件中执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">capture</code> 用于捕获输出的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">cycle</code> 用于循环一组字符串的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">include</code> 用于包含其他文件的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">unless</code> 用于在不满足指定条件时执行代码的结构。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">while</code> 用于在满足指定条件时执行代码的结构。</li>\\n</ul>\\n\\n<h3 id=\\"参考\\">参考:</h3>\\n\\n<p>1、<a href=\\"https://learn.cloudcannon.com/jekyll/list-posts-by-category/\\">https://learn.cloudcannon.com/jekyll/list-posts-by-category/</a>\\n2、<a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>如何使用 Jekyll 基于 Github Pages 搭建个人博客</title>\\n \\t<meta name=\\"description\\" content=\\"GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>如何使用 Jekyll 基于 Github Pages 搭建个人博客</h2>\\t\\t\\n\\t<time datetime=\\"2021-12-21T15:53:57+00:00\\" class=\\"by-line\\">21 Dec 2021, 杭州 | 作者 麦克船长 | 总计 11651 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#写在前面\\" id=\\"markdown-toc-写在前面\\">写在前面</a></li>\\n <li><a href=\\"#1github上的准备\\" id=\\"markdown-toc-1github上的准备\\">1、GitHub 上的准备</a></li>\\n <li><a href=\\"#2了解ruby和jekyll\\" id=\\"markdown-toc-2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</a></li>\\n <li><a href=\\"#3了解gem\\" id=\\"markdown-toc-3了解gem\\">3、了解 Gem</a></li>\\n <li><a href=\\"#4安装homebrew\\" id=\\"markdown-toc-4安装homebrew\\">4、安装 Homebrew</a></li>\\n <li><a href=\\"#5用homebrew安装ruby\\" id=\\"markdown-toc-5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</a></li>\\n <li><a href=\\"#6安装jekyll和bundler\\" id=\\"markdown-toc-6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</a></li>\\n <li><a href=\\"#7使用bundle管理包依赖关系\\" id=\\"markdown-toc-7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</a></li>\\n <li><a href=\\"#8本地启动一下看看\\" id=\\"markdown-toc-8本地启动一下看看\\">8、本地启动一下看看</a></li>\\n <li><a href=\\"#9用jekyll创建一个项目\\" id=\\"markdown-toc-9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</a></li>\\n <li><a href=\\"#10修改gemfile文件\\" id=\\"markdown-toc-10修改gemfile文件\\">10、修改 Gemfile 文件</a></li>\\n <li><a href=\\"#11配置githubpages\\" id=\\"markdown-toc-11配置githubpages\\">11、配置 Github Pages</a></li>\\n <li><a href=\\"#12配置一个jekylltheme\\" id=\\"markdown-toc-12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</a></li>\\n <li><a href=\\"#13设置自定义域名\\" id=\\"markdown-toc-13设置自定义域名\\">13、设置自定义域名</a></li>\\n <li><a href=\\"#14用-rouge-实现代码高亮\\" id=\\"markdown-toc-14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</a></li>\\n <li><a href=\\"#15一些扩展问题\\" id=\\"markdown-toc-15一些扩展问题\\">15、一些扩展问题</a> <ul>\\n <li><a href=\\"#q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\" id=\\"markdown-toc-q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</a></li>\\n <li><a href=\\"#q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\" id=\\"markdown-toc-q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</a></li>\\n <li><a href=\\"#q3如何为每篇文章添加一个目录\\" id=\\"markdown-toc-q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</a></li>\\n <li><a href=\\"#q4如何在-jekyll-中支持-katex\\" id=\\"markdown-toc-q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</a> <ul>\\n <li><a href=\\"#在-githubio-上\\" id=\\"markdown-toc-在-githubio-上\\">在 GitHub.io 上</a></li>\\n <li><a href=\\"#如果不在-githubio-上则还需要额外工作\\" id=\\"markdown-toc-如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</a></li>\\n <li><a href=\\"#使用示例\\" id=\\"markdown-toc-使用示例\\">使用示例</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#q5jekyll-中如何支持-graphviz-\\" id=\\"markdown-toc-q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</a></li>\\n <li><a href=\\"#q6如何显示--或者--\\" id=\\"markdown-toc-q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#参考\\" id=\\"markdown-toc-参考\\">参考</a></li>\\n</ul>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n\\n<p>GitHub Pages 是 GitHub 提供的免费托管静态网站的服务。使用 GitHub Pages 搭建博客,然后使用 Jekyll 生成的静态网站文件上传到该仓库。花 10 分钟时间,通过本文让你快速地实现了一个免费、简单、快速、安全、支持版本控制、支持自定义域名的独立域名博客。这样实现的优势:</p>\\n\\n<ul>\\n <li><strong>免费</strong>:GitHub Pages 允许用户免费使用其托管静态网站。</li>\\n <li><strong>简单</strong>:Jekyll 是一个轻量级的静态网站生成器,它使用简单的 Markdown 格式写文章,不需要数据库或者后端语言的支持。</li>\\n <li><strong>快速</strong>:由于 Jekyll 生成的网站是静态的,所以可以通过 CDN 加速访问速度。</li>\\n <li><strong>安全</strong>:由于 Jekyll 生成的网站是静态的,所以不存在脚本攻击、SQL 注入等安全问题。</li>\\n <li><strong>版本控制</strong>:GitHub 提供了强大的版本控制功能,你可以使用 Git 记录每一次修改,方便查看和回滚。</li>\\n <li><strong>自定义域名</strong>:你可以在仓库的设置页面中自定义域名,让你的博客更专业和个性化。</li>\\n</ul>\\n\\n<p>使用 Jekyll 和 GitHub Pages 搭建博客,你可以快速、简单、免费地拥有一个个人博客,并且可以享受到较高的安全性、版本控制和自定义域名的优势。</p>\\n\\n<p>本文涉及到 macOS 命令行的一点点基础,以及 git 版本控制软件、Web 前端的一点点基础,但是船长会尽量浅显地写在本文,避免太多其他依赖。</p>\\n\\n<h3 id=\\"1github上的准备\\">1、GitHub 上的准备</h3>\\n\\n<p>在 Github 上创建一个新的仓库,命名为「账户名.github.io」。然后将仓库拉取到本地:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git clone https://github.com/username/username.github.io\\n</code></pre></div></div>\\n\\n<p>创建一些 web 文件后再推到 Github 上就可以了:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git add <span class=\\"nt\\">--all</span>\\n<span class=\\"nv\\">$ </span>git commit <span class=\\"nt\\">-m</span> <span class=\\"s2\\">\\"Initial commit\\"</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"2了解ruby和jekyll\\">2、了解 Ruby 和 Jekyll</h3>\\n\\n<p>Ruby 目前业界的主要应用都在 Web 开发领域,有不少框架,比如 Ruby on Rails、Sinatra、Padrino. 我们这里要用到的 Jekyll 是用 Ruby 实现的一个构建静态网站的工具,用 HTML 和 Markdown 作为源码,再通过布局和模板生成网页文件。</p>\\n\\n<p>Jekyll 特别适合构建博客,支持标签、分类、搜索,并支持自定义模板和布局。</p>\\n\\n<h3 id=\\"3了解gem\\">3、了解 Gem</h3>\\n\\n<p>Gem 是 Ruby 常用的一个管理库的工具,类似于 Pip 是 Python 常用的一个管理库的工具。</p>\\n\\n<p>为 Gem 配置国内的源,这样访问速度更快:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem sources --add https://mirrors.tuna.tsinghua.edu.cn/rubygems/ --remove https://rubygems.org/\\ngem sources -l\\n</code></pre></div></div>\\n\\n<h3 id=\\"4安装homebrew\\">4、安装 Homebrew</h3>\\n\\n<p>Homebrew 是一个专门为 macOS 设计的开源软件包管理工具,熟悉 Linux 的朋友可以把 Homebrew 理解成 macOS 的 apt-get。先安装 Homebrew:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>/bin/bash <span class=\\"nt\\">-c</span> <span class=\\"s2\\">\\"</span><span class=\\"si\\">$(</span>curl <span class=\\"nt\\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class=\\"si\\">)</span><span class=\\"s2\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>为了让 Homebrew 在国内安装快一些,可以替换下镜像源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>以上用的是阿里云的源,也可以用网易的源:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">echo</span> <span class=\\"s1\\">'export HOMEBREW_BOTTLE_DOMAIN=http://mirrors.163.com/homebrew/bottles'</span> <span class=\\"o\\">&gt;&gt;</span> ~/.bash_profile\\n</code></pre></div></div>\\n\\n<p>Homebrew 安装、卸载软件的命令都很简单,brew install wget和brew uninstall wget。</p>\\n\\n<h3 id=\\"5用homebrew安装ruby\\">5、用 Homebrew 安装 Ruby</h3>\\n\\n<p>用 Homebrew 安装 chruby 和 ruby-install</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>brew <span class=\\"nb\\">install </span>chruby ruby-install xz\\n</code></pre></div></div>\\n\\n<p>安装 Ruby 的最新版本:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby-install ruby\\n</code></pre></div></div>\\n\\n<p>这时候提示如下问题:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"o\\">&gt;&gt;&gt;</span> Updating ruby versions ...\\n<span class=\\"o\\">!!!</span> Failed to download https://raw.githubusercontent.com/postmodern/ruby-versions/master/ruby/versions.txt <span class=\\"se\\">\\\\</span>\\nto /Users/captain/.cache/ruby-install/ruby/versions.txt!\\n<span class=\\"o\\">!!!</span> Failed to download ruby versions!\\n</code></pre></div></div>\\n\\n<p>因为 raw.githubusercontent.com 在国内是被 blocked,所以用https://www.ipaddress.com查一下 IP 地址,然后修改下/etc/hosts:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ echo \\"185.199.111.133 raw.githubusercontent.com\\" &gt;&gt; /etc/hosts\\n</code></pre></div></div>\\n\\n<p>然后再运行ruby-install ruby就可以正常安装了,这个过程会非常的慢,安装完成后,配置 zsh 脚本的 .zshrc 文件以便后续可以使用 chruby:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/chruby.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"source </span><span class=\\"si\\">$(</span>brew <span class=\\"nt\\">--prefix</span><span class=\\"si\\">)</span><span class=\\"s2\\">/opt/chruby/share/chruby/auto.sh\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">echo</span> <span class=\\"s2\\">\\"chruby ruby-3.1.2\\"</span> <span class=\\"o\\">&gt;&gt;</span> ~/.zshrc <span class=\\"c\\"># run 'chruby' to see actual version</span>\\n</code></pre></div></div>\\n\\n<p>再看下 Ruby 版本对不对:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>ruby <span class=\\"nt\\">-v</span>\\n</code></pre></div></div>\\n\\n<p>Jekyll 官网要求 Ruby 版本大于 3.1.2p20.</p>\\n\\n<h3 id=\\"6安装jekyll和bundler\\">6、安装 Jekyll 和 Bundler</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"nb\\">install </span>jekyll bundler\\n</code></pre></div></div>\\n\\n<p>上面顺便安装了 Bundler,Bundler 是 Ruby 常用的管理项目依赖关系的工具,类似于 virtualenv 之于 Python,可以简化项目的包依赖管理,帮你维护一份 Gemfile 文件,里面包含了所有依赖关系。这个工具的名字叫 Bundler,使用的时候都是用这个词的动词 bundle 命令。</p>\\n\\n<h3 id=\\"7使用bundle管理包依赖关系\\">7、使用 bundle 管理包依赖关系</h3>\\n\\n<p>创建 Gemfile 文件,Gemfile 是 Ruby 项目的依赖包管理文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nb\\">source</span> <span class=\\"s1\\">'https://rubygems.org'</span>\\ngem <span class=\\"s1\\">'nokogiri'</span>\\ngem <span class=\\"s1\\">'rack'</span>, <span class=\\"s1\\">'~&gt; 2.2.4'</span>\\ngem <span class=\\"s1\\">'rspec'</span>\\ngem <span class=\\"s1\\">'jekyll'</span>\\n</code></pre></div></div>\\n\\n<p>然后安装依赖包,这里默认会根据运行命令时所在的目录的 Gemfile 来安装:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<p>Gemfile.lock 是 Gemfile 的锁定版本,记录了当前项目所使用的所有依赖包的版本信息。下面把这两个文件都加入到 Git 版本控制中。</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ git add Gemfile Gemfile.lock\\n</code></pre></div></div>\\n\\n<h3 id=\\"8本地启动一下看看\\">8、本地启动一下看看</h3>\\n\\n<p>先用 bundle 如下命令来启动:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: none\\n Source: /Users/captain/Workspace/poechant.github.io\\n Destination: /Users/captain/Workspace/poechant.github.io/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n done in 0.014 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io'\\n Server address: http://127.0.0.1:4000\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>然后打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>这就说明 Jekyll 本地配置已经成功了。然后把当前的版本同步到 Git 上:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>git pull <span class=\\"nt\\">--no-rebase</span>\\n<span class=\\"nv\\">$ </span>git push <span class=\\"nt\\">-u</span> origin main\\n</code></pre></div></div>\\n\\n<h3 id=\\"9用jekyll创建一个项目\\">9、用 Jekyll 创建一个项目</h3>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll new CaptainMikeBlog\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>CaptainMikeBlog\\n<span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>启动日志如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Configuration file: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_config.yml\\n Source: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog\\n Destination: /Users/captain/Workspace/poechant.github.io/CaptainMikeBlog/_site\\n Incremental build: disabled. Enable with --incremental\\n Generating... \\n Jekyll Feed: Generating feed for posts\\n done in 0.365 seconds.\\n Auto-regeneration: enabled for '/Users/captain/Workspace/poechant.github.io/CaptainMikeBlog'\\n Server address: http://127.0.0.1:4000/\\n Server running... press ctrl-c to stop.\\n</code></pre></div></div>\\n\\n<p>再打开浏览器输入http://localhost:4000看看效果:</p>\\n\\n<p><img src=\\"/img/src/2022-12-21-build-github-pages-with-jekyll-2.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"10修改gemfile文件\\">10、修改 Gemfile 文件</h3>\\n\\n<p>注释掉gem ”jekyll”开头的这一行,修改# gem ”github-pages”开头的这一行为:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>gem <span class=\\"s2\\">\\"github-pages\\"</span>, <span class=\\"s2\\">\\"~&gt; GITHUB-PAGES-VERSION\\"</span>, group: :jekyll_plugins\\n</code></pre></div></div>\\n\\n<p>其中的GITHUB-PAGES-VERSION改为具体的版本号,版本号参考https://pages.github.com/versions/,我写本文的时候github-pages最新版本号是227。关闭 Gemfile 文件然后命令行运行如下命令:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>$ bundle install\\n</code></pre></div></div>\\n\\n<p>再本地启动服务器测试:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>jekyll server\\n</code></pre></div></div>\\n\\n<p>得到如下提示:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>You have already activated i18n 1.12.0, but your Gemfile requires i18n 0.9.5.\\nPrepending <span class=\\"sb\\">`</span>bundle <span class=\\"nb\\">exec</span><span class=\\"sb\\">`</span> to your <span class=\\"nb\\">command </span>may solve this. <span class=\\"o\\">(</span>Gem::LoadError<span class=\\"o\\">)</span>\\n</code></pre></div></div>\\n\\n<p>参考https://github.com/Homebrew/brew.sh/issues/845这个 issue 后如下解决:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>bundle add webrick\\n<span class=\\"nv\\">$ </span>bundle <span class=\\"nb\\">exec </span>jekyll serve\\n</code></pre></div></div>\\n\\n<p>这里注意jekyll server和bundle exec jekyll serve两个的区别是前者基本本地 Jekyll 版本启动服务,后者基于目录下的 Gemfile 文件启动服务,所以我们要用后者。</p>\\n\\n<h3 id=\\"11配置githubpages\\">11、配置 Github Pages</h3>\\n\\n<p>在 Github 的仓库页面进入「Settings - Code and Automation - Pages - Build and Deploy」,选择「Deploy from a branch」,然后选择你设定的分支。再选发布源的文件夹,这里我设置为根目录。然后「保存」。再修改 _config.yml 文件:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>baseurl: \\"\\"\\nurl: \\"http://your-username.github.io\\"\\n</code></pre></div></div>\\n\\n<p>将本地代码push到 Github 仓库中,在浏览器访问your-username.github.io即可,有时候可能要等几分钟。</p>\\n\\n<h3 id=\\"12配置一个jekylltheme\\">12、配置一个 Jekyll Theme</h3>\\n\\n<p>可以在http://jekyllthemes.org/这个网站上找一下喜欢的 theme,下载后将如下文件都 copy 到你项目目录下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>_includes\\n_layouts\\n_sass\\ncss\\njs\\nimg\\n404.markdown\\nindex.html\\n</code></pre></div></div>\\n\\n<p>不同主题会有所不同,这里只列个大概。</p>\\n\\n<h3 id=\\"13设置自定义域名\\">13、设置自定义域名</h3>\\n\\n<p>添加四条 A 记录,记录值如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>185.199.108.153\\n185.199.109.153\\n185.199.110.153\\n185.199.111.153\\n</code></pre></div></div>\\n\\n<p>添加 CNAME,主机记录为www,记录值为your-username.github.io。然后在「Github 你的仓库里 - Settings - Pages - Custom Domain」填写你刚使用的域名,并把Enforce HTTPS打上勾。</p>\\n\\n<p>一旦解析成功,Github 上会自动多一个 CNAME 文件。把你最新的代码都 push 到 Github 仓库上,稍等片刻就可以从你自己的域名访问 Github Pages 搭建的博客啦。</p>\\n\\n<h3 id=\\"14用-rouge-实现代码高亮\\">14、用 rouge 实现代码高亮</h3>\\n\\n<p>我们用支持 Markdown 内代码语法高亮的 Rouge 来实现,首先安装 Rouge:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem install kramdom rouge\\n</code></pre></div></div>\\n\\n<p>然后配置 _config.yml 文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">markdown</span><span class=\\"pi\\">:</span> <span class=\\"s\\">kramdown</span>\\n<span class=\\"na\\">highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n\\n<span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">input</span><span class=\\"pi\\">:</span> <span class=\\"s\\">GFM</span>\\n <span class=\\"na\\">syntax_highlighter</span><span class=\\"pi\\">:</span> <span class=\\"s\\">rouge</span>\\n</code></pre></div></div>\\n\\n<p>然后用 rouge 创建 syntax.css 文件:</p>\\n\\n<div class=\\"language-bash highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>rougify style github <span class=\\"o\\">&gt;</span> css/syntax.css\\n</code></pre></div></div>\\n\\n<p>在 <code class=\\"language-plaintext highlighter-rouge\\">_include/head.html</code> 文件中添加:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/css/syntax.css\\"</span> <span class=\\"nt\\">/&gt;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"15一些扩展问题\\">15、一些扩展问题</h3>\\n\\n<h4 id=\\"q1我想在网站的首页的每一篇文章标题下显示一个指定的摘要而不是自动从文章内容开头截取的应该如何实现呢\\">Q1:我想在网站的首页的每一篇文章标题下,显示一个指定的摘要,而不是自动从文章内容开头截取的,应该如何实现呢?</h4>\\n\\n<p>在 Jekyll 中,你可以在每篇文章的 front matter 中设置摘要字段。例如,你可以在文章的 front matter 中添加一个 excerpt 字段,然后在该字段中填入你想要在首页显示的摘要内容。</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是一篇文章</span>\\n<span class=\\"na\\">excerpt</span><span class=\\"pi\\">:</span> <span class=\\"s\\">这是文章的摘要</span>\\n<span class=\\"nn\\">---</span>\\n\\n这是文章的正文内容\\n</code></pre></div></div>\\n\\n<p>然后,在你的首页模板中,你可以使用``输出文章的摘要。例如:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nt\\">&lt;ul&gt;</span>\\n {% for post in paginator.posts %}\\n <span class=\\"nt\\">&lt;li&gt;</span>\\n <span class=\\"nt\\">&lt;h2&gt;&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{{ post.url }}\\"</span><span class=\\"nt\\">&gt;</span>{{ post.title }}<span class=\\"nt\\">&lt;/a&gt;&lt;/h2&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>{{ post.excerpt }}<span class=\\"nt\\">&lt;/p&gt;</span>\\n <span class=\\"nt\\">&lt;/li&gt;</span>\\n {% endfor %}\\n<span class=\\"nt\\">&lt;/ul&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样,在首页显示文章列表时,每篇文章就会带上它的摘要内容。</p>\\n\\n<p>注意,如果文章的 excerpt 字段没有设置,那么在首页显示时就不会有摘要内容。因此,建议在发布新文章时务必检查 excerpt 字段是否已经设置。</p>\\n\\n<h4 id=\\"q2如何支持对每一个分类都可以显示一个该分类下的所有文章的页面\\">Q2:如何支持对每一个分类都可以显示一个该分类下的所有文章的页面?</h4>\\n\\n<p>有很多种办法,但是这里我讲一个比较简单且容易维护的方法,不过也有其弊端。首先在<code class=\\"language-plaintext highlighter-rouge\\">_layouts</code>目录下创建一个<code class=\\"language-plaintext highlighter-rouge\\">category.html</code>文件:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>---\\nlayout: default\\n---\\n\\n<span class=\\"nt\\">&lt;div</span> <span class=\\"na\\">class=</span><span class=\\"s\\">\\"container\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n {% if site.categories[page.category] %}\\n {% for post in site.categories[page.category] %}\\n <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"{% if site.baseurl == \\"</span><span class=\\"err\\">/\\"</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">else</span> <span class=\\"err\\">%}{{</span> <span class=\\"na\\">post.url</span> <span class=\\"err\\">|</span> <span class=\\"na\\">prepend:</span> <span class=\\"na\\">site.baseurl</span> <span class=\\"err\\">}}{%</span> <span class=\\"na\\">endif</span> <span class=\\"err\\">%}\\"</span><span class=\\"nt\\">&gt;</span>\\n {%if post.header %}{{ post.header }}{% else %}{{ post.title }}{% endif %}\\n <span class=\\"nt\\">&lt;/a&gt;</span>\\n {% endfor %}\\n {% else %}\\n <span class=\\"nt\\">&lt;br&gt;</span>\\n <span class=\\"nt\\">&lt;p&gt;</span>No posts for this category. If you have something in mind, check <span class=\\"nt\\">&lt;a</span> <span class=\\"na\\">href=</span><span class=\\"s\\">\\"/write\\"</span><span class=\\"nt\\">&gt;</span>Write For Us<span class=\\"nt\\">&lt;/a&gt;</span>page.<span class=\\"nt\\">&lt;/p&gt;</span>\\n {% endif %}\\n<span class=\\"nt\\">&lt;/div&gt;</span>\\n</code></pre></div></div>\\n\\n<p>这样就有了一个可以显示某个 category 下的所有 posts 的布局文件了。然后修改<code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">include</span><span class=\\"pi\\">:</span> <span class=\\"pi\\">[</span><span class=\\"s1\\">'</span><span class=\\"s\\">_categories'</span><span class=\\"pi\\">]</span>\\n</code></pre></div></div>\\n\\n<p>在根目录创建一个<code class=\\"language-plaintext highlighter-rouge\\">categories</code>目录,并在里面对每个 category 分别创建一个 html 文件,文件名即 category 的名字。但这个文件特别的简单,就是只需要写一个头部,例如我的「AI」分类的<code class=\\"language-plaintext highlighter-rouge\\">ai.html</code>如下:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nn\\">---</span>\\n<span class=\\"na\\">layout</span><span class=\\"pi\\">:</span> <span class=\\"s\\">category</span>\\n<span class=\\"na\\">title</span><span class=\\"pi\\">:</span> <span class=\\"s\\">人工智能</span>\\n<span class=\\"na\\">description</span><span class=\\"pi\\">:</span> <span class=\\"s\\">This is the description.</span>\\n<span class=\\"na\\">permalink</span><span class=\\"pi\\">:</span> <span class=\\"s\\">/category/ai</span>\\n<span class=\\"na\\">category</span><span class=\\"pi\\">:</span> <span class=\\"s\\">ai</span>\\n<span class=\\"na\\">category_type</span><span class=\\"pi\\">:</span> <span class=\\"s\\">tech</span>\\n<span class=\\"nn\\">---</span>\\n</code></pre></div></div>\\n\\n<p>那么之后每次创建文件时,在头部写<code class=\\"language-plaintext highlighter-rouge\\">category</code>一定要与这些<code class=\\"language-plaintext highlighter-rouge\\">categories</code>中的<code class=\\"language-plaintext highlighter-rouge\\">html</code>文件对应起来。</p>\\n\\n<h4 id=\\"q3如何为每篇文章添加一个目录\\">Q3:如何为每篇文章添加一个目录</h4>\\n\\n<p>这个是 Markdown 可以解决的,并不涉及 Jekyll,对于 Jekyll 的 Markdown 引擎可以用如下极其简单的方式实现:</p>\\n\\n<div class=\\"language-markdown highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">*</span> TOC\\n{:toc}\\n</code></pre></div></div>\\n\\n<h4 id=\\"q4如何在-jekyll-中支持-katex\\">Q4:如何在 Jekyll 中支持 KaTeX</h4>\\n\\n<p>Katex 是一个开源的 JavaScript 库,能够在浏览器端快速渲染 LaTeX 格式的数学公式。</p>\\n\\n<h5 id=\\"在-githubio-上\\">在 GitHub.io 上</h5>\\n\\n<p>先修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code>:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">kramdown</span><span class=\\"pi\\">:</span>\\n <span class=\\"na\\">math_engine</span><span class=\\"pi\\">:</span> <span class=\\"s\\">katex</span>\\n</code></pre></div></div>\\n\\n<p>然后修改 <code class=\\"language-plaintext highlighter-rouge\\">_includes/head.html</code> 文件,在 <code class=\\"language-plaintext highlighter-rouge\\">&lt;head&gt;</code> 与 <code class=\\"language-plaintext highlighter-rouge\\">&lt;/head&gt;</code> 中间:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\">&lt;!--KaTeX--&gt;</span>\\n <span class=\\"nt\\">&lt;link</span> <span class=\\"na\\">rel=</span><span class=\\"s\\">\\"stylesheet\\"</span>\\n <span class=\\"na\\">href=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script </span><span class=\\"na\\">defer</span>\\n <span class=\\"na\\">src=</span><span class=\\"s\\">\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\"</span>\\n <span class=\\"na\\">integrity=</span><span class=\\"s\\">\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\"</span>\\n <span class=\\"na\\">crossorigin=</span><span class=\\"s\\">\\"anonymous\\"</span><span class=\\"nt\\">&gt;&lt;/script&gt;</span>\\n <span class=\\"nt\\">&lt;script&gt;</span>\\n <span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">addEventListener</span><span class=\\"p\\">(</span><span class=\\"dl\\">\\"</span><span class=\\"s2\\">DOMContentLoaded</span><span class=\\"dl\\">\\"</span><span class=\\"p\\">,</span> <span class=\\"kd\\">function</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"nx\\">renderMathInElement</span><span class=\\"p\\">(</span><span class=\\"nb\\">document</span><span class=\\"p\\">.</span><span class=\\"nx\\">body</span><span class=\\"p\\">,</span> <span class=\\"p\\">{</span>\\n <span class=\\"c1\\">// ...options...</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"p\\">});</span>\\n <span class=\\"nt\\">&lt;/script&gt;</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"如果不在-githubio-上则还需要额外工作\\">如果不在 GitHub.io 上,则还需要额外工作</h5>\\n\\n<p>以上方式只适合于 GitHub.io 的网站,如果是自己搭建的网站用 Jekyll 则要自己安装,如下:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>gem <span class=\\"nb\\">install </span>kramdom-math-katex\\n\\ngem <span class=\\"nb\\">install </span>katex\\ngem <span class=\\"nb\\">install </span>execjs\\n\\ngem <span class=\\"nb\\">install </span>therubyracer\\ngem <span class=\\"nb\\">install </span>therubyrhino\\ngem <span class=\\"nb\\">install </span>duktape\\n</code></pre></div></div>\\n\\n<h5 id=\\"使用示例\\">使用示例</h5>\\n\\n<p>以如下方式输入输入如下内容:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}\\n$$ \\\\sum_{i=1}^{n} a_i $$\\n{% endraw %}\\n</code></pre></div></div>\\n\\n<p>就会得到一个数学公式:</p>\\n\\n\\\\[\\\\sum_{i=1}^{n} a_i\\\\]\\n\\n<h4 id=\\"q5jekyll-中如何支持-graphviz-\\">Q5:Jekyll 中如何支持 Graphviz ?</h4>\\n\\n<p>这要依赖 <code class=\\"language-plaintext highlighter-rouge\\">jekyll-graphviz-dot</code>,修改 <code class=\\"language-plaintext highlighter-rouge\\">Gemfile</code> 增加一句:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>group :jekyll_plugins <span class=\\"k\\">do\\n </span>gem <span class=\\"s2\\">\\"jekyll-graphviz-dot\\"</span>\\nend\\n</code></pre></div></div>\\n\\n<p>再修改 <code class=\\"language-plaintext highlighter-rouge\\">_config.yml</code> 配置文件:</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">plugins</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"s\\">jekyll-graphviz</span>\\n</code></pre></div></div>\\n\\n<p>再在本地安装 graphviz,可以通过 <code class=\\"language-plaintext highlighter-rouge\\">conda install graphviz</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">brew install graphviz</code>。然后 <code class=\\"language-plaintext highlighter-rouge\\">bundle install</code> 再 <code class=\\"language-plaintext highlighter-rouge\\">bundle exec jekyll serve</code> 在本地下一段看看效果:</p>\\n\\n<pre><code class=\\"language-graphviz\\">{% graph some graph title %}\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n{% endgraph %}\\n</code></pre>\\n\\n<p>如果看到如下效果,就说明你都配置成功了:</p>\\n\\n<div class=\\"graphviz-wrapper\\">\\n\\n<!-- Generated by graphviz version 2.43.0 (0)\\n -->\\n<!-- Title: G Pages: 1 -->\\n<svg role=\\"img\\" aria-label=\\"some graph title\\" width=\\"89pt\\" height=\\"188pt\\" viewBox=\\"0.00 0.00 89.00 188.00\\">\\n<title>some graph title</title>\\n<desc>\\ndigraph G {\\n a -&gt; b\\n b -&gt; c\\n c -&gt; a\\n}\\n</desc>\\n\\n<g id=\\"graph0\\" class=\\"graph\\" transform=\\"scale(1 1) rotate(0) translate(4 184)\\">\\n<title>G</title>\\n<polygon fill=\\"white\\" stroke=\\"transparent\\" points=\\"-4,4 -4,-184 85,-184 85,4 -4,4\\" />\\n<!-- a -->\\n<g id=\\"node1\\" class=\\"node\\">\\n<title>a</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-162\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-158.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">a</text>\\n</g>\\n<!-- b -->\\n<g id=\\"node2\\" class=\\"node\\">\\n<title>b</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"27\\" cy=\\"-90\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"27\\" y=\\"-86.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">b</text>\\n</g>\\n<!-- a&#45;&gt;b -->\\n<g id=\\"edge1\\" class=\\"edge\\">\\n<title>a&#45;&gt;b</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M47.6,-144.41C44.49,-136.34 40.67,-126.43 37.17,-117.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"40.4,-116.03 33.54,-107.96 33.87,-118.55 40.4,-116.03\\" />\\n</g>\\n<!-- c -->\\n<g id=\\"node3\\" class=\\"node\\">\\n<title>c</title>\\n<ellipse fill=\\"none\\" stroke=\\"black\\" cx=\\"54\\" cy=\\"-18\\" rx=\\"27\\" ry=\\"18\\" />\\n<text text-anchor=\\"middle\\" x=\\"54\\" y=\\"-14.3\\" font-family=\\"Times,serif\\" font-size=\\"14.00\\">c</text>\\n</g>\\n<!-- b&#45;&gt;c -->\\n<g id=\\"edge2\\" class=\\"edge\\">\\n<title>b&#45;&gt;c</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M33.4,-72.41C36.51,-64.34 40.33,-54.43 43.83,-45.35\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"47.13,-46.55 47.46,-35.96 40.6,-44.03 47.13,-46.55\\" />\\n</g>\\n<!-- c&#45;&gt;a -->\\n<g id=\\"edge3\\" class=\\"edge\\">\\n<title>c&#45;&gt;a</title>\\n<path fill=\\"none\\" stroke=\\"black\\" d=\\"M57.65,-36.09C59.68,-46.43 61.98,-59.91 63,-72 64.34,-87.94 64.34,-92.06 63,-108 62.28,-116.5 60.93,-125.69 59.49,-133.99\\" />\\n<polygon fill=\\"black\\" stroke=\\"black\\" points=\\"56.03,-133.44 57.65,-143.91 62.91,-134.71 56.03,-133.44\\" />\\n</g>\\n</g>\\n</svg>\\n</div>\\n\\n<p>但是 GitHub Pages 默认并不支持 Graphviz 插件,所以还需要如下处理:</p>\\n\\n<h4 id=\\"q6如何显示--或者--\\">Q6:如何显示 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 或者 <code class=\\"language-plaintext highlighter-rouge\\">{{</code> ?</h4>\\n\\n<p>其实也是一个字符转义的问题,我们直接面对一个在 StackOverflow 上会被问的终极 Jekyll 中 Markdown 转义问题(与 Liquid Template Tags 冲突的问题),如何实现显示 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 呢?方法如下:</p>\\n\\n<div class=\\"language-html highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>{% raw %}{%{% endraw %} raw %}\\n{% raw %}{%{% endraw %} endraw %}\\n</code></pre></div></div>\\n\\n<p>如上,就是用 <code class=\\"language-plaintext highlighter-rouge\\">{% raw %}</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">{% endraw %}</code> 把 <code class=\\"language-plaintext highlighter-rouge\\">{%</code> 包起来,但是 <code class=\\"language-plaintext highlighter-rouge\\">%}</code> 不用包。应该讲的很清楚了吧。</p>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<ol>\\n <li><a href=\\"https://bundler.io\\">https://bundler.io</a></li>\\n <li><a href=\\"https://jekyllrb.com/docs/\\">https://jekyllrb.com/docs/</a></li>\\n <li><a href=\\"https://zhuanlan.zhihu.com/p/87225594\\">https://zhuanlan.zhihu.com/p/87225594</a></li>\\n <li><a href=\\"https://chat.openai.com/chat\\">https://chat.openai.com/chat</a></li>\\n <li><a href=\\"https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll\\">https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site\\">https://docs.github.com/zh/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site</a></li>\\n <li><a href=\\"https://github.com/dyutibarma/monochrome\\">https://github.com/dyutibarma/monochrome</a></li>\\n <li><a href=\\"https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain\\">https://docs.github.com/zh/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#configuring-a-subdomain</a></li>\\n <li><a href=\\"http://www.seanbuscay.com/blog/jekyll-toc-markdown/\\">http://www.seanbuscay.com/blog/jekyll-toc-markdown/</a></li>\\n <li><a href=\\"https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/\\">https://www.xuningyang.com/blog/2021-01-11-katex-with-jekyll/</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n <li><a href=\\"https://github.com/DerekStride/jekyll-graphviz\\">https://github.com/DerekStride/jekyll-graphviz</a></li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"],"ai":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</title>\\n \\t<meta name=\\"description\\" content=\\"火出圈的 ChatGPT,背后是自然语言处理领域近几年发展的成果。本文从近几年自然语言处理的关键发展脉络,过程中关键的几篇学术论文,这几年的所有重要行业里程碑,以及目前为止业内已经诞生的应用。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>自然语言处理 AIGC 近年的发展脉络、关键论文、技术里程碑和商业应用</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-24T15:08:01+00:00\\" class=\\"by-line\\">24 Dec 2022, 杭州 | 作者 麦克船长 | 总计 8879 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<ul>\\n <li>作者:麦克船长(钟超)</li>\\n <li>微信:sinosuperman</li>\\n</ul>\\n\\n<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一自然语言处理领域近年的发展关键节点\\" id=\\"markdown-toc-一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</a> <ul>\\n <li><a href=\\"#1从理性主义到经验主义\\" id=\\"markdown-toc-1从理性主义到经验主义\\">1、从理性主义到经验主义</a></li>\\n <li><a href=\\"#2经验主义的早期还不是深度学习\\" id=\\"markdown-toc-2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</a></li>\\n <li><a href=\\"#3撇开特征让机器囫囵吞枣地学吧\\" id=\\"markdown-toc-3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</a></li>\\n <li><a href=\\"#4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\" id=\\"markdown-toc-4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</a></li>\\n <li><a href=\\"#5自监督学习法让我们省去人工标注\\" id=\\"markdown-toc-5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</a></li>\\n <li><a href=\\"#6用原始的任务训练出来的模型能迁移去解决新任务吗\\" id=\\"markdown-toc-6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</a></li>\\n <li><a href=\\"#7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\" id=\\"markdown-toc-7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</a></li>\\n <li><a href=\\"#8数据和算力有了还不够\\" id=\\"markdown-toc-8数据和算力有了还不够\\">8、数据和算力有了,还不够</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二学术里程碑几篇重量级论文\\" id=\\"markdown-toc-二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</a> <ul>\\n <li><a href=\\"#1提出-transformer-的attention-is-all-you-need2017\\" id=\\"markdown-toc-1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</a></li>\\n <li><a href=\\"#2elmo-deep-contextualized-word-representations\\" id=\\"markdown-toc-2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</a></li>\\n <li><a href=\\"#3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\" id=\\"markdown-toc-3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</a></li>\\n <li><a href=\\"#4gpt-3-language-models-are-few-shot-learners2020\\" id=\\"markdown-toc-4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</a></li>\\n <li><a href=\\"#其他的重量级论文\\" id=\\"markdown-toc-其他的重量级论文\\">其他的重量级论文</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三行业里程碑\\" id=\\"markdown-toc-三行业里程碑\\">三、行业里程碑</a></li>\\n <li><a href=\\"#四成本\\" id=\\"markdown-toc-四成本\\">四、成本</a></li>\\n <li><a href=\\"#五业内应用\\" id=\\"markdown-toc-五业内应用\\">五、业内应用</a></li>\\n <li><a href=\\"#五行业内哪些人的言论值得我们日常重点关注\\" id=\\"markdown-toc-五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</a></li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一自然语言处理领域近年的发展关键节点\\">一、自然语言处理领域近年的发展关键节点</h3>\\n\\n<p><img src=\\"/img/src/2022-12-17-ai-bert-1-1.jpg\\" alt=\\"image\\" /></p>\\n\\n<h4 id=\\"1从理性主义到经验主义\\">1、从理性主义到经验主义</h4>\\n\\n<p>自然语言处理(Natural Language Processing,简称 NLP),一开始走的是专家路线,也就是想「白盒化」来解构对自然语言的理解,这被称为「符号主义(Symbolism)」。符号主义的背后,是人类对自己用符号系统基于逻辑来完全数字化自然语言的自信。反正这条路目前是没走出来,你要非说「这其实是自负」,暂时人工智能专家们也无可辩驳。沿着这个路径的研究一直占据人工智能主流到 20 世纪 90 年代。</p>\\n\\n<p>这里我们想想,自然语言处理,其实是两个过程,一个是输入,即对自然语言的理解,一个是输出,即近期有点火的概念 AIGC(Artificial Intelligence Generated Content)。我们这里说说前者,人类学习语言的过程,哪有什么符号系统,哪有什么逻辑,就是被疯狂输入,然后经过很多个月之后,一个小 baby 就学会说话了,这个过程没有「理性主义」的痕迹,只有「经验主义」的胜利。那么 AI 学人话,能这样吗?</p>\\n\\n<p>于是就有了所谓「联结主义(Connectionism)」:你知道人的神经元网络吧?这个是一个个神经元,相互联结组成一个网络,通过这个网络来非常「黑盒化」地学习自然语言。至于这个网络里的每一个细节,我们不甚清楚,但就是可以通过这个网络模型学会自然语言,这就是一种「经验主义」。从 20 世纪 90 年代,人工智能领域就是沿着这个方向取得了巨大进展的。要注意一点,经验主义地路径解决 NLP 问题,并不等同于神经网络,但它是目前最有效的。</p>\\n\\n<h4 id=\\"2经验主义的早期还不是深度学习\\">2、经验主义的早期,还不是深度学习</h4>\\n\\n<p>最初的经验主义,还是主要通过人工对特征进行「经验性地」提取,对计算机来说不要让它求甚解,直接给它喂这些梳理好的「特征」就好了。而这个需要一定的专业领域知识储备,加上人工地提取特征的操作过程,被称为「特征工程」。</p>\\n\\n<p>可以看出来,「特征工程」的人工工作量非常大,可以说是名副其实的「人工」智能了(此处捂脸)。但这已经比此前的、有点理想的那种构建符号系统的想法,要务实多了,也确实在解决问题的实用主义上也好得多。以这个为主流的研究,大概持续到 2010 年代。</p>\\n\\n<h4 id=\\"3撇开特征让机器囫囵吞枣地学吧\\">3、撇开特征,让机器「囫囵吞枣」地学吧</h4>\\n\\n<p>要经过「人工」对特征进行研究、提取,实在是太难了,你说是「经验主义」,其实我个人认为有点介于「理性主义」与「经验主义」之间。毕竟还是非常需要人进行非常专家级地梳理的。于是,更囫囵个儿地给机器喂数据,让机器学会的方向,逐渐成为主流。能这样的前提,是牛逼算力的大发展,以及海量数据集的大规模沉淀,所以才会在 2010 年代爆发。</p>\\n\\n<p>这囫囵吞枣的学法,目前主要都是基于深度神经网路的表示学习方法实现的。为啥说「深度神经网络」,因为「从输入到输出」是有一层又一层的神经网络,第一层接收原始的自然语言输入,这么多层的神经网络就被称为深度神经网络。这个过程显著地避免了「特征工程」的人工高成本。</p>\\n\\n<h4 id=\\"4囫囵个儿地学习省去特征工程的人工但也少不了标注的人工\\">4、囫囵个儿地学习,省去特征工程的人工,但也少不了标注的人工</h4>\\n\\n<p>虽然省去了需要专家的「特征工程」,但是这个「囫囵个儿学习法」还是需要依赖标注数据的,也就是「监督学习」。通过先学习大量有人工标注地数据,构建好深度神经网络后,再对测试数据进行验证,最后再用于使用。能不能把人工标注也给省了?或者至少不需要海量标注吧。</p>\\n\\n<h4 id=\\"5自监督学习法让我们省去人工标注\\">5、自监督学习法,让我们省去人工标注</h4>\\n\\n<p>大家上中学的时候做过英语试卷里的「完形填空」吗?为什么我们根据一个填空的上下文,能推测出这个空应该填什么词?那我们是不是可以根据这个原理,把一段段完整的文字内容挖词进行训练学习?没错,这个挖掉的词,就可以当做曾经的「人工标注」,上年文就是训练数据。但是需要海量的数据,怎么办?</p>\\n\\n<p>好在书籍、互联网网页是我们最好的数据来源,而且数据量极其巨大,于是这就解决了人工个标注问题。由此衍生出来的方法,就被成为「自监督学习(Self-Supervised Learning)」。</p>\\n\\n<h4 id=\\"6用原始的任务训练出来的模型能迁移去解决新任务吗\\">6、用原始的任务训练出来的模型,能迁移去解决新任务吗?</h4>\\n\\n<p>这是一个迁移学习问题,这也就引出了「预训练(Pre-Training)」,最近火到出圈的「ChatGPT」最后两个字母「PT」就是「预训练」。正如「预训练」这个名字,我们先对一些原始任务用大量数据对一个模型进行训练(这个过程其实就叫预训练),然后对于实际要解决的各种任务,再使用少量数据对模型进行精调(Fine-Tune),从而得到一个解决具体问题的模型。</p>\\n\\n<p>这样的方式,让面对具体任务(可以叫下游任务,或者目标任务)时可以省去很多训练,所以对这种模型叫做「预训练模型」。因此上游任务的训练,就变得非常有复用性、通用性价值,而不是每次面对新任务构建新模型来训练。沿着预训练模型,NLP 取得了非常多的突破。这个技术趋势,是从 2017 年 Transformer 模型在论文《Attention is All You Need》被提出后开始的,在论文中作者使用了大量的未标记的语言数据进行自监督学习,以学习 Transformer 模型的语言表示。然后,在这个自监督学习的模型的基础上,再使用少量的标记数据进行进一步训练,以解决具体的目标任务。</p>\\n\\n<h4 id=\\"7从理解到生成nlp-是最直面-aigc-最硬核难题的领域\\">7、从理解到生成,NLP 是最直面 AIGC 最硬核难题的领域</h4>\\n\\n<p>我们再说回到前面提到的人工标注,从这点来理解所谓「任务」。人工标注,是主观性很强的。在图像处理、语音识别两个领域,标注数据的复用性很强,所以可以积累大的数据标注集,这是有积累沉淀价值的,比如 CV 领域鼎鼎大名的 ImageNet 图像数据集。但是 NLP 领域的任务复杂、多样,很难像图像处理、语音识别那样单纯地得到大量有价值标注。什么意思呢?这与我们在不同领域面对的任务有关。</p>\\n\\n<p>比如给一副画,对于绝大多数需要输入这幅画的任务来说,标注出它是一副油画、作者梵高、画中有星空等等,都是必须的。比如对于一个人脸识别,哪里是眼睛、鼻子、嘴巴,也是从任务层面非常通用的。语音识别就更有通用性了。但是对于一句自然语言,一个随机的任务需要什么信息,这非常难以沉淀通用。</p>\\n\\n<p>从这个角度说,一个「图像处理」任务一般是要输出这个图像里有什么内容,一个「语音识别」任务一般是要输出这段语音的文字内容是什么。但是一个「自然语言处理」任务一般是要干嘛?鬼知道要干嘛,但肯定大多数时候是要先生成一段话作为回应,这也就是「自然语言生成」。</p>\\n\\n<p>所以 NLP 领域的 NLG(Natural Language Generation)面对着最多可能性的任务,也就是最直面 AIGC 核心问题的领域。</p>\\n\\n<h4 id=\\"8数据和算力有了还不够\\">8、数据和算力有了,还不够</h4>\\n\\n<p>我个人认为,预训练这个方向之所以正确,就是因为它在推动 AGI(Artificial General Intelligent)。这背后是一个基本哲学问题:我们应该把劲儿使在推动 AGI,还是应该认为每个领域都应该有自己独有的模型?</p>\\n\\n<p>这个问题的答案,在我看来是笃定的。AI 目前面对的还是人类思考的问题,而人面对的问题去构建的人脑学习模型,并没有呈现出在不同领域里人脑的学习方式有显著差异,更何况计算机能容纳的学习能力显然更广、更深。因此我很笃定,我们一定是要构建 AGI,为什么 AGI 将解决我们方方面面的问题。</p>\\n\\n<p>那么一个预训练模型,在下游能解决的问题越广,越说明这是在构建 AGI。但是反过来对上游的预训练模型的要求,就是它最好模型参数越多越好,这样能容纳的下游任务也就可能越多样。因此我们现在知道的 ChatGPT 背后的 OpenAI 公司此前研发的 GPT-3 已经有 1750 亿个参数了,这就是 —— 大模型。</p>\\n\\n<p>所以目前沿着预训练方向发展的自然语言处理领域,已经进入了「大模型、大数据、大算力」时代。</p>\\n\\n<h3 id=\\"二学术里程碑几篇重量级论文\\">二、学术里程碑:几篇重量级论文</h3>\\n\\n<p>以下重量级的论文,每一篇都不短,B 站上有一些二手解读,虽然二手但是也值得高效地看下,这些论文我罗列如下。我的理解也不深,欢迎随时交流。</p>\\n\\n<h4 id=\\"1提出-transformer-的attention-is-all-you-need2017\\">1、提出 Transformer 的《Attention is All You Need》(2017)</h4>\\n\\n<p>Google 的 Lamda、BERT,OpenAI 的 GPT-3 都是基于 Transformer 的。</p>\\n\\n<p>《Attention is all you need》是一篇颇具影响力的自然语言处理(NLP)论文,由 Google 在 2017 年发表。这篇论文提出了一种叫做 Transformer 的模型架构,这种模型架构不依赖于递归神经网络(RNN)或卷积神经网络(CNN)等传统的深度学习架构,而是使用了注意力机制(attention mechanism)和多头注意力(multi-head attention)来捕捉序列间的依赖关系。</p>\\n\\n<p>看到有人说「<strong>Transformer 基本宣告了 LSTM 在 NLP 领域的终结</strong>」。Transformer 模型在 NLP 领域内获得了广泛的应用,并且因为其较好的并行化能力,在计算资源有限的情况下也能够获得较好的性能。Transformer 模型也被广泛应用于其他领域,如计算机视觉、音频处理等。</p>\\n\\n<h4 id=\\"2elmo-deep-contextualized-word-representations\\">2、ELMo: Deep contextualized word representations</h4>\\n\\n<p>ELMo 是 Embeddings from Language Models 的缩写,刚好是《芝麻街》中一个角色的名字,是在 Peters 等人于 2018 年在 ACL(美国计算机学会计算语言学会议,NLP 领域顶级会议之一)上发表的论文《Deep contextualized word representations》中被提出来的。</p>\\n\\n<p>ELMo 是一种预训练模型,基于深度双向递归神经网络(biLSTM),可以用来生成词嵌入(word embeddings)。ELMo 使用了大量未标记的文本数据训练,并使用了多层双向递归神经网络来学习。</p>\\n\\n<h4 id=\\"3bert-pre-training-of-deep-bidirectional-transformers-for-language-understanding2018\\">3、BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)</h4>\\n\\n<p>BERT 模型是在一篇于 2018 年发表的叫做《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》的论文中被提出来的,BERT 是 Bidirectional Encoder Representations from Transformers 的缩写。我觉得这个名字有点硬凑出来的意思,BERT 也是《芝麻街》里一个角色的名字,我想就是为了跟 ELMo 凑一块儿怕它孤单吧。这篇论文带来的最大突破性变化有:</p>\\n\\n<ul>\\n <li>在语言模型预训练中引入双向信息:传统的预训练语言模型(比如 word2vec、GloVe)通常只考虑了单向的信息(前面的词语)。BERT 模型则同时考虑了前后的词语,从而更好地捕捉句子的上下文信息。</li>\\n <li>在预训练中引入自监督学习任务。</li>\\n</ul>\\n\\n<p>关于 BERT,我这里写了一篇背景介绍、用例试跑、优劣势分析:<a href=\\"https://www.mikecaptain.com/2022/12/17/ai-bert-1/\\">《你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例》</a></p>\\n\\n<h4 id=\\"4gpt-3-language-models-are-few-shot-learners2020\\">4、GPT-3: Language Models are Few-Shot Learners(2020)</h4>\\n\\n<p>这篇来自 OpenAI 的论文,提出了「小样本学习(Few-Shot Learning,FSL)」的新训练方法,可以在小样本的情况下取得优秀的表现。</p>\\n\\n<h4 id=\\"其他的重量级论文\\">其他的重量级论文</h4>\\n\\n<ul>\\n <li>Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context(2019)</li>\\n <li>RoBERTa: A Robustly Optimized BERT Pretraining Approach(2019)</li>\\n <li>T5: Exploring the Limits of Transfer Learning witha Unified Text-to-Text Transformer(2020)</li>\\n <li>ViT: An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale(2021)</li>\\n <li>ERNIE-ViL: Vision and Language Pre-training for Image Captioning and VQA(2021)</li>\\n <li>……</li>\\n</ul>\\n\\n<h3 id=\\"三行业里程碑\\">三、行业里程碑</h3>\\n\\n<p>2017 年 8 月,Andrej Karpathy 在其 Twitter 上发文称「很遗憾,梯度下降(实现的 AI 模型)代码写得比你好」。同年 11 月 Andrej 在博客上表示,软件 2.0 将会区别于软件 1.0 时代,程序将由更抽象的、基于神经网络权重的程序语言编写。</p>\\n\\n<p>2018 年 OpenAI 推出了无监督的、基于强化学习的第一代 GPT。</p>\\n\\n<p>2019 年情人节,OpenAI 发布 GPT-2,当时被称为史上最强的「通用」自然语言处理模型,基于 Transformer,拥有 15 亿个参数,使用含有 800 万网页内容的数据集训练。</p>\\n\\n<p>2020 年 6 月,拥有 1750 亿个参数的 GPT-3 面世,这个模型的训练量是 GPT-2 的十倍不止,并开放了商业化 API 共使用,不到一年时间发展出约 300 家企业客户。</p>\\n\\n<p>2021 年 6 月,微软与 OpenAI 共同推出代码辅助生成 AI 工具 GitHub Copilot.</p>\\n\\n<p>2022 年 1 月,OpenAI 发布基于 GPT-3 微调的模型 InstructGPT(包括 text-davinci-001、text-davinci-002、text-davinci-003),微调主要来自于 RLHF(Reinforcement Learning via Human Feedback)。</p>\\n\\n<p>2022 年 5 月,杭州 AI 领域初创公司「感知阶跃(ZMO.ai)」宣布完成由高瓴资本领投、GGV Capital 和 GSR Ventures 跟投的 800 万美元 A 轮融资。</p>\\n\\n<p>2022 年 10 月 19 日,Jasper.ai 宣布完成由 Insight Partner 领投,Coatue、(BVP)Bessemer 以及 IVP 等机构跟投的 1.25 亿美元 A 轮融资,估值达到了 15 亿美元,Jasper AI 从产品上线至今仅 18 个月。</p>\\n\\n<p>2022 年 11 月底,OpenAI 推出基于 GPT-3.5 的 ChatGPT 对话系统,震惊全球。项目地址:https://chat.openai.com 。</p>\\n\\n<p>2022 年 12 月底,专注于各 AI 闭源项目的逆向工程的 Philip Wang 发布了 PaLM+RLHF 的文本生成开源模型,类似于 ChatGPT。该项目基于 Google 的大型语言模型 PaLM 和带有人类反馈的强化学习(RLHF),拥有 5400 亿个参数。项目地址:https://github.com/lucidrains/PaLM-rlhf-pytorch 。</p>\\n\\n<h3 id=\\"四成本\\">四、成本</h3>\\n\\n<p>目前成本主要有三方面:大模型、大数据、大算力。这其中最昂贵的成本首先是算力。下面有几个数据可以作为参照:</p>\\n\\n<ul>\\n <li>2020 年的一项研究表明,开发一个只有 15 亿个参数的文本生成模型的费用高达 160 万美元。</li>\\n <li>2022 年 7 月,为了训练拥有 1760 亿个参数的开源模型 Bloom,Hugging Face 的研究人员耗时三个月,使用了 384 个英伟达 A100 GPU。</li>\\n <li>OpenAI 的文本生成 GPT-3(具有大约 1750 亿个参数)的运行成本约为每年 87,000 美元。</li>\\n <li>Hugging Face 训练 Bloom 花了三个月的时间。</li>\\n</ul>\\n\\n<h3 id=\\"五业内应用\\">五、业内应用</h3>\\n\\n<p>因为图片生成的容错率非常高,也就是在应用上的包容度更高,相比之下文本或语音的生成,是对结果容错非常低的,比如不容许事实错误、逻辑错误等等。这类的应用,我们能想到:</p>\\n\\n<ul>\\n <li>虚拟客服(可以乱真的)</li>\\n <li>智能助理:AI 家庭教师、AI 非诉律师、AI 医生助手、AI 新闻编辑</li>\\n <li>智能翻译</li>\\n <li>智能导购员:如果叠加虚拟人技术、语音合成技术,可以应用于电商</li>\\n <li>AI 广告公司:替代传统广告公司</li>\\n <li>AI 程序员助手:更高智能的辅助代码生成</li>\\n <li>部分场景下的美术工作者:游戏素材生成、海报生成</li>\\n</ul>\\n\\n<p>我们可以看到,AI 带来的这一波机会,都是曾经常说的「人不会被 AI 替代」的领域,也就是一些创作创意创新型工作,其中的中低端部分会因为成本因素而极力推动 AI 应用的发展。</p>\\n\\n<p>所以下面除了大家耳熟能详的 CV 领域的 AIGC 产品 Disco Diffusion、MidJourney、DALL·E 2、Stable Diffusion 之外,我们重点关注非图片生成类的应用。</p>\\n\\n<ul>\\n <li>用于营销场景的 AI 写手与图像生成工具 Jasper.ai,常被用于生成互联网营销文案(比如用于 Instagram、Tik Tok、Facebook、博客、email、论坛帖子 等等)。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-7.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年 6 月,微软与 OpenAI 共同推出的的代码辅助生成 AI 工具 GitHub Copilot(https://github.com/features/copilot) 发布。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-2.jpg\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>虚拟客服 DialogFlow,能理解电话、语音内容等输入,并且给出文本或语音合成的输出。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-8.png\\" alt=\\"image\\" /></p>\\n\\n<ul>\\n <li>2021 年年底,西湖心辰公司发布「<a href=\\"https://www.heyfriday.cn/\\">Friday AI 智能协作系统</a>」,并且目前也做了商业化。</li>\\n</ul>\\n\\n<p><img src=\\"/img/src/2022-12-24-captain-nlp-1.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"五行业内哪些人的言论值得我们日常重点关注\\">五、行业内哪些人的言论值得我们日常重点关注</h3>\\n\\n<p>这些人的言论都值得我们关注:Sam Altman、Andrej Karpathy、Elon Musk。</p>\\n\\n<p>Andrej Karpathy 在其 Medium 博客上提到:</p>\\n\\n<blockquote>\\n <p>我们都熟悉的软件 1.0 的「经典堆栈」(The classical stack)是由 Python、C++ 等语言编写的,它由程序员编写的明确的计算机指令组成。通过编写每一行代码,程序员标识了程序空间中具有某些期望行为的特定点。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>相比之下,软件 2.0 是用更抽象、不友好的人类语言(如神经网络的权重)编写的,没有人参与编写这些代码,因为权重数量很多(典型的网络可能有数百万个),并且直接用权重编写代码有一定困难(我尝试过)。</p>\\n</blockquote>\\n\\n<p>不过打那之后 Andrej 在其博客上就再未说过一句话。</p>\\n\\n<p>OpenAI 创始人兼 CEO Sam Altman 曾表示:</p>\\n\\n<blockquote>\\n <p>十年前的传统观点认为,人工智能首先会影响体力劳动,然后是认知劳动,再然后,也许有一天可以做创造性工作。现在看起来,它会以相反的顺序进行。</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>通用人工智能的建成会比大多数人想象得更快,并且它会改变大多数人想象中的一切。」</p>\\n</blockquote>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://beta.openai.com/docs/models</li>\\n <li>https://karpathy.medium.com/software-2-0-a64152b37c35</li>\\n <li>https://hub.baai.ac.cn/view/21726</li>\\n <li>https://www.reddit.com/r/OpenAI/comments/zdrnsf/comment/iz3kfui/?context=3</li>\\n <li>https://www.sohu.com/a/615541698_121255906</li>\\n <li>http://blog.itpub.net/29829936/viewspace-2654536/</li>\\n <li>http://tech.sina.com.cn/csj/2018-10-13/doc-ihmhafir3634167.shtml</li>\\n <li>https://colab.research.google.com/github/alembics/disco-diffusion/blob/main/Disco_Diffusion.ipynb#scrollTo=DefMidasFns</li>\\n <li>https://en.wikipedia.org/wiki/BERT_(language_model)</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</title>\\n \\t<meta name=\\"description\\" content=\\"2018 年 Google 发布了 BERT 模型后迅速席卷 NLP 领域,这家伙可是比 ChatGPT 背后的 GPT 还要早的。本文简单介绍了 BERT 后主要是希望大家都手试一下,所以文中提到了一个小的中文模型供大家练手,以及一个小用例。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-17T15:08:01+00:00\\" class=\\"by-line\\">17 Dec 2022, 杭州 | 作者 麦克船长 | 总计 7275 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一关于-bert-的一些背景\\" id=\\"markdown-toc-一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</a></li>\\n <li><a href=\\"#二开始一个-bert-的动手小试验\\" id=\\"markdown-toc-二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</a> <ul>\\n <li><a href=\\"#1安装-anaconda-来为部署-bert-做环境准备\\" id=\\"markdown-toc-1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</a></li>\\n <li><a href=\\"#2安装-bert-所需要的各种依赖\\" id=\\"markdown-toc-2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</a></li>\\n <li><a href=\\"#3下载一个预训练pre-train过的-bert-模型\\" id=\\"markdown-toc-3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</a></li>\\n <li><a href=\\"#5启动-bert-服务端\\" id=\\"markdown-toc-5启动-bert-服务端\\">5、启动 BERT 服务端</a></li>\\n <li><a href=\\"#6在-pycharm-中使用-conda-的环境\\" id=\\"markdown-toc-6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</a></li>\\n <li><a href=\\"#7编写程序实现-bert-客户端\\" id=\\"markdown-toc-7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三bert-模型的优劣势及其原因\\" id=\\"markdown-toc-三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</a> <ul>\\n <li><a href=\\"#1bert-的优势是很明显的\\" id=\\"markdown-toc-1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</a> <ul>\\n <li><a href=\\"#11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\" id=\\"markdown-toc-11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</a></li>\\n <li><a href=\\"#12识别并专注于较重要的部分进行文本处理\\" id=\\"markdown-toc-12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</a></li>\\n <li><a href=\\"#13快速构建针对具体任务的-nlp-系统\\" id=\\"markdown-toc-13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2bert-模型的劣势及其原因\\" id=\\"markdown-toc-2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</a> <ul>\\n <li><a href=\\"#21随机挖-mask-的完形填空题是有隐患的\\" id=\\"markdown-toc-21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</a></li>\\n <li><a href=\\"#22nsp-任务有必要吗\\" id=\\"markdown-toc-22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</a></li>\\n <li><a href=\\"#23针对两个或以上词组成的连续词的词义被丢失\\" id=\\"markdown-toc-23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</a></li>\\n <li><a href=\\"#24需要的算力高\\" id=\\"markdown-toc-24需要的算力高\\">2.4、需要的算力高</a></li>\\n <li><a href=\\"#25需要的模型大\\" id=\\"markdown-toc-25需要的模型大\\">2.5、需要的模型大</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四一些关于-bert-的问题\\" id=\\"markdown-toc-四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</a> <ul>\\n <li><a href=\\"#1bert-模型的所谓双向与-bilstm-的双向是啥区别\\" id=\\"markdown-toc-1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</a></li>\\n <li><a href=\\"#2为什么-bert-可以比-rnn-更好地并行化\\" id=\\"markdown-toc-2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#reference\\" id=\\"markdown-toc-reference\\">Reference</a></li>\\n</ul>\\n\\n<h3 id=\\"一关于-bert-的一些背景\\">一、关于 BERT 的一些背景</h3>\\n\\n<p>2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。</p>\\n\\n<p>BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。</p>\\n\\n<p>BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。</p>\\n\\n<h3 id=\\"二开始一个-bert-的动手小试验\\">二、开始一个 BERT 的动手小试验</h3>\\n\\n<p>为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。</p>\\n\\n<h4 id=\\"1安装-anaconda-来为部署-bert-做环境准备\\">1、安装 Anaconda 来为部署 BERT 做环境准备</h4>\\n\\n<p>先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。\\n我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。</p>\\n\\n<p>更新 conda 到最新版本:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda update <span class=\\"nt\\">-n</span> base conda\\n</code></pre></div></div>\\n\\n<p>使用 Python 3.7 创建一个新的环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda create <span class=\\"nt\\">-n</span> py37 <span class=\\"nv\\">python</span><span class=\\"o\\">=</span>3.7\\n</code></pre></div></div>\\n\\n<p>激活这个新环境:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda activate py37\\n</code></pre></div></div>\\n\\n<p>验证正在使用的是正确版本的 Python</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>python <span class=\\"nt\\">--version</span>\\n</code></pre></div></div>\\n\\n<p>另外你可能还会用到的 conda 命令有:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 你之后一定会需要 deactivate 一个环境,命令如下:</span>\\nconda deactivate py37\\n\\n<span class=\\"c\\"># 查看 conda 当前安装的所有库</span>\\nconda list\\n</code></pre></div></div>\\n\\n<h4 id=\\"2安装-bert-所需要的各种依赖\\">2、安装 BERT 所需要的各种依赖</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>conda <span class=\\"nb\\">install </span><span class=\\"nv\\">tensorflow</span><span class=\\"o\\">==</span>1.14.0\\n</code></pre></div></div>\\n\\n<p>验证 tensorflow 是否安装正确:</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">import</span> <span class=\\"nn\\">tensorflow</span> <span class=\\"k\\">as</span> <span class=\\"n\\">tf</span>\\n<span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"n\\">tf</span><span class=\\"p\\">.</span><span class=\\"n\\">__version__</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3下载一个预训练pre-train过的-bert-模型\\">3、下载一个预训练(Pre-Train)过的 BERT 模型</h4>\\n\\n<p>官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models</p>\\n\\n<p>也有一些中文的模型,以下是 ChatGPT 推荐的三个:</p>\\n\\n<ul>\\n <li>BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n <li>ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。</li>\\n <li>RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。</li>\\n</ul>\\n\\n<p>4、安装 BERT 的服务端和客户端</p>\\n\\n<p>这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。</p>\\n\\n<p>在你激活的环境里,安装 <code class=\\"language-plaintext highlighter-rouge\\">bert-as-service</code>:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 安装服务端和客户端</span>\\n<span class=\\"c\\"># 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html</span>\\nconda <span class=\\"nb\\">install </span>bert-serving-server bert-serving-client \\n验证 bert-as-service 是否安装成功\\nbert-serving-start <span class=\\"nt\\">-h</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5启动-bert-服务端\\">5、启动 BERT 服务端</h4>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c\\"># 命令行下启动BERT服务</span>\\n<span class=\\"c\\"># -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待</span>\\nbert-serving-start <span class=\\"nt\\">-model_dir</span> /模型/的/绝对/路径 <span class=\\"nt\\">-num_worker</span><span class=\\"o\\">=</span>4\\n</code></pre></div></div>\\n\\n<h4 id=\\"6在-pycharm-中使用-conda-的环境\\">6、在 PyCharm 中使用 Conda 的环境</h4>\\n\\n<p>在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。</p>\\n\\n<p>接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的<code class=\\"language-plaintext highlighter-rouge\\">「/Users/captain/opt/anaconda3/bin/python」</code>。</p>\\n\\n<p>配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/</p>\\n\\n<h4 id=\\"7编写程序实现-bert-客户端\\">7、编写程序实现 BERT 客户端</h4>\\n\\n<p>这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126</p>\\n\\n<div class=\\"language-python highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kn\\">from</span> <span class=\\"nn\\">bert_serving.client</span> <span class=\\"kn\\">import</span> <span class=\\"n\\">BertClient</span>\\n<span class=\\"kn\\">import</span> <span class=\\"nn\\">numpy</span> <span class=\\"k\\">as</span> <span class=\\"n\\">np</span>\\n\\n<span class=\\"c1\\"># 定义类\\n</span><span class=\\"k\\">class</span> <span class=\\"nc\\">BertModel</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">__init__</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"k\\">try</span><span class=\\"p\\">:</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertClient</span><span class=\\"p\\">(</span><span class=\\"n\\">ip</span><span class=\\"o\\">=</span><span class=\\"s\\">'127.0.0.1'</span><span class=\\"p\\">,</span> <span class=\\"n\\">port</span><span class=\\"o\\">=</span><span class=\\"mi\\">5555</span><span class=\\"p\\">,</span> <span class=\\"n\\">port_out</span><span class=\\"o\\">=</span><span class=\\"mi\\">5556</span><span class=\\"p\\">)</span> <span class=\\"c1\\"># 创建客户端对象\\n</span> <span class=\\"c1\\"># 注意:可以参考API,查看其它参数的设置\\n</span> <span class=\\"c1\\"># 127.0.0.1 表示本机IP,也可以用localhost\\n</span> <span class=\\"k\\">except</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">raise</span> <span class=\\"nb\\">Exception</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cannot create BertClient\\"</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">close_bert</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">):</span>\\n <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">text</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''对输入文本进行embedding\\n Args:\\n text: str, 输入文本\\n Returns:\\n text_vector: float, 返回一个列表,包含text的embedding编码值\\n '''</span>\\n <span class=\\"n\\">text_vector</span> <span class=\\"o\\">=</span> <span class=\\"bp\\">self</span><span class=\\"p\\">.</span><span class=\\"n\\">bert_client</span><span class=\\"p\\">.</span><span class=\\"n\\">encode</span><span class=\\"p\\">([</span><span class=\\"n\\">text</span><span class=\\"p\\">])[</span><span class=\\"mi\\">0</span><span class=\\"p\\">]</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">text_vector</span> <span class=\\"c1\\"># 获取输出结果\\n</span>\\n <span class=\\"k\\">def</span> <span class=\\"nf\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"bp\\">self</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_1</span><span class=\\"p\\">,</span> <span class=\\"n\\">vec_2</span><span class=\\"p\\">):</span>\\n <span class=\\"s\\">'''根据两个语句的vector,计算它们的相似性\\n Args:\\n vec_1: float, 语句1的vector\\n vec_2: float, 语句2的vector\\n Returns:\\n sim_value: float, 返回相似性的计算值\\n '''</span>\\n <span class=\\"c1\\"># 根据cosine的计算公式\\n</span> <span class=\\"n\\">v1</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_1</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">v2</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">mat</span><span class=\\"p\\">(</span><span class=\\"n\\">vec_2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">float</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span> <span class=\\"o\\">*</span> <span class=\\"n\\">v2</span><span class=\\"p\\">.</span><span class=\\"n\\">T</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v1</span><span class=\\"p\\">)</span> <span class=\\"o\\">*</span> <span class=\\"n\\">np</span><span class=\\"p\\">.</span><span class=\\"n\\">linalg</span><span class=\\"p\\">.</span><span class=\\"n\\">norm</span><span class=\\"p\\">(</span><span class=\\"n\\">v2</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">cosine</span> <span class=\\"o\\">=</span> <span class=\\"n\\">a</span> <span class=\\"o\\">/</span> <span class=\\"n\\">b</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">cosine</span>\\n\\n\\n<span class=\\"k\\">if</span> <span class=\\"n\\">__name__</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"__main__\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># 创建bert对象\\n</span> <span class=\\"n\\">bert</span> <span class=\\"o\\">=</span> <span class=\\"n\\">BertModel</span><span class=\\"p\\">()</span>\\n <span class=\\"k\\">while</span> <span class=\\"bp\\">True</span><span class=\\"p\\">:</span>\\n <span class=\\"c1\\"># --- 输入语句 ----\\n</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句1: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">if</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"N\\"</span> <span class=\\"ow\\">or</span> <span class=\\"n\\">input_a</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"n\\"</span><span class=\\"p\\">:</span>\\n <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">close_bert</span><span class=\\"p\\">()</span> <span class=\\"c1\\"># 关闭服务\\n</span> <span class=\\"k\\">break</span>\\n\\n <span class=\\"n\\">input_b</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">input</span><span class=\\"p\\">(</span><span class=\\"s\\">'请输入语句2: '</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># --- 对输入语句进行embedding ---\\n</span>\\n <span class=\\"n\\">a_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_a</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'a_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">a_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"n\\">b_vec</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">sentence_embedding</span><span class=\\"p\\">(</span><span class=\\"n\\">input_b</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'b_vec shape : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">.</span><span class=\\"n\\">shape</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 计算两个语句的相似性\\n</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">=</span> <span class=\\"n\\">bert</span><span class=\\"p\\">.</span><span class=\\"n\\">caculate_similarity</span><span class=\\"p\\">(</span><span class=\\"n\\">a_vec</span><span class=\\"p\\">,</span> <span class=\\"n\\">b_vec</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'cosine value : '</span><span class=\\"p\\">,</span> <span class=\\"n\\">cos</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">'</span><span class=\\"se\\">\\\\n\\\\n</span><span class=\\"s\\">'</span><span class=\\"p\\">)</span>\\n\\n <span class=\\"c1\\"># 如果相似性值大于0.85,则输出相似,否则,输出不同\\n</span> <span class=\\"k\\">if</span> <span class=\\"n\\">cos</span> <span class=\\"o\\">&gt;</span> <span class=\\"mf\\">0.85</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"2个语句的含义相似\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">else</span><span class=\\"p\\">:</span>\\n <span class=\\"k\\">print</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"不相似\\"</span><span class=\\"p\\">)</span>\\n</code></pre></div></div>\\n\\n<p>在使用 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 连接 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 时,你需要确保 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-server</code> 使用的模型和 <code class=\\"language-plaintext highlighter-rouge\\">bert-serving-client</code> 使用的模型是匹配的,否则会出现错误。</p>\\n\\n<p>程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>请输入语句1: \\n请输入语句2: \\n</code></pre></div></div>\\n\\n<p>两句输入好确认后,得到如下形式的结果:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>a_vec shape : (768,)\\nb_vec shape : (768,)\\ncosine value : 0.8691698561422959\\n</code></pre></div></div>\\n\\n<p>其实这个小试验蛮没意思的,而且准确性也比较令人质疑。</p>\\n\\n<h3 id=\\"三bert-模型的优劣势及其原因\\">三、BERT 模型的优劣势及其原因</h3>\\n\\n<p>论文地址:<a href=\\"https://arxiv.org/abs/1810.04805\\">《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》</a> 。</p>\\n\\n<h4 id=\\"1bert-的优势是很明显的\\">1、BERT 的优势是很明显的</h4>\\n\\n<p>复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:</p>\\n\\n<blockquote>\\n <p>证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。</p>\\n</blockquote>\\n\\n<h5 id=\\"11mlm-和-nsp-预训练能够捕捉到自然语言中的各种复杂细节\\">1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节</h5>\\n\\n<p>因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。</p>\\n\\n<p>具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。</p>\\n\\n<h5 id=\\"12识别并专注于较重要的部分进行文本处理\\">1.2、识别并专注于较重要的部分进行文本处理</h5>\\n\\n<p>这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。</p>\\n\\n<h5 id=\\"13快速构建针对具体任务的-nlp-系统\\">1.3、快速构建针对具体任务的 NLP 系统</h5>\\n\\n<p>因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。</p>\\n\\n<h4 id=\\"2bert-模型的劣势及其原因\\">2、BERT 模型的劣势及其原因</h4>\\n\\n<h5 id=\\"21随机挖-mask-的完形填空题是有隐患的\\">2.1、随机挖 MASK 的完形填空题是有隐患的</h5>\\n\\n<p>对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。</p>\\n\\n<h5 id=\\"22nsp-任务有必要吗\\">2.2、NSP 任务有必要吗?</h5>\\n\\n<p>论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。</p>\\n\\n<h5 id=\\"23针对两个或以上词组成的连续词的词义被丢失\\">2.3、针对两个或以上词组成的连续词的词义被丢失</h5>\\n\\n<p>比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。</p>\\n\\n<h5 id=\\"24需要的算力高\\">2.4、需要的算力高</h5>\\n\\n<p>算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。</p>\\n\\n<h5 id=\\"25需要的模型大\\">2.5、需要的模型大</h5>\\n\\n<p>模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。</p>\\n\\n<h3 id=\\"四一些关于-bert-的问题\\">四、一些关于 BERT 的问题</h3>\\n\\n<h4 id=\\"1bert-模型的所谓双向与-bilstm-的双向是啥区别\\">1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?</h4>\\n\\n<p>BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。</p>\\n\\n<h4 id=\\"2为什么-bert-可以比-rnn-更好地并行化\\">2、为什么 BERT 可以比 RNN 更好地并行化</h4>\\n\\n<p>RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。</p>\\n\\n<h3 id=\\"reference\\">Reference</h3>\\n\\n<ol>\\n <li>https://arxiv.org/abs/1810.04805</li>\\n <li>https://github.com/google-research/bert</li>\\n <li>https://github.com/ymcui/Chinese-BERT-wwm</li>\\n <li>https://zhuanlan.zhihu.com/p/195723105</li>\\n <li>https://www.jiqizhixin.com/articles/2018-10-24-13</li>\\n</ol>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</title>\\n \\t<meta name=\\"description\\" content=\\"最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是「Optimizing Language Models for Dialogue」,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>动动手,让你和你的朋友们,在微信上跟 ChatGPT 聊聊天</h2>\\t\\t\\n\\t<time datetime=\\"2022-12-11T15:59:57+00:00\\" class=\\"by-line\\">11 Dec 2022, 杭州 | 作者 麦克船长 | 总计 1692 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><img src=\\"/img/src/2022-12-11-wechat-chatgpt-3.png\\" alt=\\"image\\" /></p>\\n\\n<h3 id=\\"写在前面\\">写在前面</h3>\\n<p>最近 OpenAI 的 ChatGPT 非常地出圈,ChatGPT 是一个由 OpenAI 训练的大型语言模型,被设计用来回答用户的问题并提供信息。官方的 Slogan 是 <strong>「Optimizing Language Models for Dialogue」</strong>,所以非常适合做到 IM 里聊天。那么我在想如果用一个微信号,背后是 ChatGPT,是不是很有趣?正当我准备利用 WeChaty 开发一个服务端程序来连接 ChatGPT 时,发现目前 Github 上已经有人做了,刚好可以省去很多工程的工作。</p>\\n\\n<h3 id=\\"stepbystep\\">Step by step</h3>\\n\\n<p>本实践依赖:CLI、Docker、npm、Github、fuergaosi233/wechat-chatgpt、git、YAML、Chrome 的使用。以下将简洁地 Step by step 列出步骤。</p>\\n\\n<p>第一步,你要现有一个 OpenAI 的账号,注意注册时手机号不能是中国大陆或香港的,IP 地址和 GPS 也不能暴露你是中国大陆或者香港的。</p>\\n\\n<p>第二步,准备一台服务器(否则个人电脑要一直处于开机运行状态),由于后面将用到 Session Token 来登录,因此 IP 地址是香港也没关系,于是我是在我的香港服务器上部署 wechat-chatgpt</p>\\n\\n<p>第三步,在服务器上安装 Docker,不赘述。</p>\\n\\n<p>第四步,从 Github 上拉取项目项目到服务器上。</p>\\n\\n<p>第五步,任何设备上登录 ChatGPT,用 Chrome 的 Inspect 来查看并复制 session token 到剪贴板。</p>\\n\\n<p>第六步,编辑 wechat-chatgpt 的 config.yaml,填写 session token;设置 private trigger keywords(可选)。</p>\\n\\n<div class=\\"language-yaml highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"na\\">chatGPTAccountPool</span><span class=\\"pi\\">:</span>\\n <span class=\\"pi\\">-</span> <span class=\\"na\\">email</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your email&gt;</span>\\n <span class=\\"na\\">password</span><span class=\\"pi\\">:</span> <span class=\\"s\\">&lt;your password&gt;</span>\\n<span class=\\"c1\\"># if you hope only some keywords can trigger chatgpt on private chat, you can set it like this:</span>\\n<span class=\\"na\\">chatPrivateTiggerKeyword</span><span class=\\"pi\\">:</span> <span class=\\"s2\\">\\"</span><span class=\\"s\\">\\"</span>\\n</code></pre></div></div>\\n\\n<p>第七步,用 docker 来拉取 wechat-chatgpt</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker pull holegots/wechat-chatgpt:latest。\\n</code></pre></div></div>\\n\\n<p>第八步,启动 wechat-chatgpt:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker run <span class=\\"nt\\">-d</span> <span class=\\"nt\\">--name</span> wechat-chatgpt <span class=\\"nt\\">-v</span> <span class=\\"si\\">$(</span><span class=\\"nb\\">pwd</span><span class=\\"si\\">)</span>/config.yaml:/app/config.yaml holegots/wechat-chatgpt:latest\\n</code></pre></div></div>\\n\\n<p>注意,如果手动模式下也可以用npm run dev启动。如果提示系统不认识 npm 则可以运行 <code class=\\"language-plaintext highlighter-rouge\\">npm install &amp;&amp; poetry install</code> 来解决。到此你就可以在微信上跟这个打通了 ChatGPT 的账号聊天了。</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-1.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n <th><img src=\\"/img/src/2022-12-11-wechat-chatgpt-2.png\\" alt=\\"image\\" style=\\"width:100%\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>其实可以看到这个 AI 船长不管是专业性问题(计算机相关)还是非专业问题,都回答的很不错。</p>\\n\\n<p>如何停止、重启、查看日志呢?首先停止的命令是docker stop wechat-chatgpt,登录时需要扫码登录微信并追踪 logs,因为这其实是用了微信在桌面端的接口。</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker logs <span class=\\"nt\\">-f</span> wechat-chatgpt\\n</code></pre></div></div>\\n\\n<p>会在 Terminal 里显示一个文字阵列组成的桌面端微信登录二维码,用你打算做成微信 AI 机器人那个微信号扫一下,相关信息都填完。另外,这样最好别用自己的微信大号,而是用一个小号。微信不让聊这些,小号注意要完成实名认证。</p>\\n\\n<p>如果要停止运行,用如下命令:</p>\\n\\n<div class=\\"language-shell highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>docker stop wechat-chatgpt\\n</code></pre></div></div>\\n\\n<h3 id=\\"参考\\">参考</h3>\\n\\n<p>1、<a href=\\"https://github.com/fuergaosi233/wechat-chatgpt/tree/main\\">https://github.com/fuergaosi233/wechat-chatgpt/tree/main</a></p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</title>\\n \\t<meta name=\\"description\\" content=\\"AIGC,MidJourney,Image2Text,文生图\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>确实惊艳!用 MidJourney 三分钟生成了两张 CG 级高清机甲特写</h2>\\t\\t\\n\\t<time datetime=\\"2022-11-30T15:12:03+00:00\\" class=\\"by-line\\">30 Nov 2022, 杭州 | 作者 麦克船长 | 总计 387 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p>因为 Diffusion 模型在计算机视觉领域的发展,最近文生图(Text2Image)很火,花了三分钟时间用 MidJourney 做了一组机甲图,确实非常惊艳,直接看图:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-1.png\\" alt=\\"image\\" /></th>\\n <th><img src=\\"/img/src/2022-12-16-midjourney-first-test-2.png\\" alt=\\"image\\" /></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td> </td>\\n <td> </td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>今年人工智能在 CV 领域的发展非常的精彩,目前市面上看到的主要应用,都是这种松散式的、对结果容错率很高图像生成,基于一段 prompt 生成一张或一组图片,甚至已经有了 avatarai.me 这种帮你打造全套的 photorealistic 层次质感的全套图片和视频商业化产品。</p>\\n\\n<p><img src=\\"/img/src/2022-12-16-midjourney-first-test-3.png\\" alt=\\"image\\" />\\n(<em>注:MidJourney 官网</em>)</p>\\n\\n<p>未来很快,我们将看到一些更精准满足图像生成需求的应用出现,比如生成游戏素材(其实现在已经有了,比如 Scenario.gg)、AI 替身生成等等。</p>\\n\\n<p>相应的,对抗性的防御技术也会很快发展。</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n"]},"collections":[{"relative_directory":"_posts","docs":["<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</title>\\n \\t<meta name=\\"description\\" content=\\"RTMFP 是 Adobe 开发的基于 UDP 协议的实时传输媒体流协议,支持 P2P 传输,具有较高的实时性和安全性。它的主要应用场景是视频通信、语音通信和网络游戏。OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。Cumulus 是一个基于 OpenRTMFP 的服务器,提供 RTMFP 服务。它具有轻量级、跨平台和可扩展的特点,并且还提供了负载均衡和可扩展性解决方案。YY 语音的 Web 端音视频流媒体能力,正是基于 RTMFP 协议做的迭代优化实现的。本文是船长关于这个系列文章的第一篇。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 1:入门介绍、部署与 Hello World</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-09T18:57:19+00:00\\" class=\\"by-line\\">09 Apr 2012, 广州 | 作者 麦克船长 | 总计 8401 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一rtmfp是什么\\" id=\\"markdown-toc-一rtmfp是什么\\">一、RTMFP 是什么?</a> <ul>\\n <li><a href=\\"#文件分享-p2p-和实时流媒体-p2p-的区别是什么\\" id=\\"markdown-toc-文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</a></li>\\n <li><a href=\\"#rtmfp-和-rtmp-之间的区别是什么\\" id=\\"markdown-toc-rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</a></li>\\n <li><a href=\\"#flash-player-支持-rtmfp-吗\\" id=\\"markdown-toc-flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</a></li>\\n <li><a href=\\"#cumulus-使用-adobe-的-cirrus-key-吗\\" id=\\"markdown-toc-cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</a></li>\\n <li><a href=\\"#这个开源项目合法吗\\" id=\\"markdown-toc-这个开源项目合法吗\\">这个开源项目合法吗?</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二openrtmfp和cumulus\\" id=\\"markdown-toc-二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</a></li>\\n <li><a href=\\"#三入门介绍与部署cumulusserver\\" id=\\"markdown-toc-三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</a> <ul>\\n <li><a href=\\"#1背景介绍\\" id=\\"markdown-toc-1背景介绍\\">1、背景介绍</a></li>\\n <li><a href=\\"#2准备工作\\" id=\\"markdown-toc-2准备工作\\">2、准备工作</a></li>\\n <li><a href=\\"#3安装\\" id=\\"markdown-toc-3安装\\">3、安装</a> <ul>\\n <li><a href=\\"#31外部依赖的安装\\" id=\\"markdown-toc-31外部依赖的安装\\">3.1、外部依赖的安装</a></li>\\n <li><a href=\\"#32安装openrtmfpcumulus\\" id=\\"markdown-toc-32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#4配置\\" id=\\"markdown-toc-4配置\\">4、配置</a></li>\\n <li><a href=\\"#5启动\\" id=\\"markdown-toc-5启动\\">5、启动</a></li>\\n <li><a href=\\"#6基本使用\\" id=\\"markdown-toc-6基本使用\\">6、基本使用</a></li>\\n <li><a href=\\"#7扩展cumulusserverserverapplication\\" id=\\"markdown-toc-7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三用lua编写helloworld应用扩展cumulusserver\\" id=\\"markdown-toc-三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</a> <ul>\\n <li><a href=\\"#1server-side\\" id=\\"markdown-toc-1server-side\\">1、Server-side</a> <ul>\\n <li><a href=\\"#11serverconfiguration\\" id=\\"markdown-toc-11serverconfiguration\\">1.1、Server configuration</a></li>\\n <li><a href=\\"#12applicationfile\\" id=\\"markdown-toc-12applicationfile\\">1.2、Application file</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2client-side\\" id=\\"markdown-toc-2client-side\\">2、Client-side</a></li>\\n <li><a href=\\"#3运行结果\\" id=\\"markdown-toc-3运行结果\\">3、运行结果</a></li>\\n <li><a href=\\"#4远程测试一个免费的测试服务器\\" id=\\"markdown-toc-4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<h3 id=\\"一rtmfp是什么\\">一、RTMFP 是什么?</h3>\\n\\n<p>Real-Time Media Flow Protocol(RTMFP)是 Adobe 开发的一种基于 UDP 并支持 P2P 的实时传输媒体流。主要特点是:高传输效率(可以使用压缩和算法来优化流量从而提高传输效率)、高实时性(可以保证媒体流的实时性使得视频通信和其他实时通信更加流畅)、支持 P2P 传输(减少对服务器的依赖从而减少带宽和服务器资源消耗)、高安全性(加密媒体流从而保证其安全性)。</p>\\n\\n<p>RTMFP 的主要应用场景包括:视频通信(视频聊天和视频会议)、语音通信(语音聊天、电话)、网络游戏。不过 RTMFP 目前仅有 Adobe 开发的版本,所以它并不是个开源项目,而是个商业化服务。那么有没有开源版本呢?</p>\\n\\n<h4 id=\\"文件分享-p2p-和实时流媒体-p2p-的区别是什么\\">文件分享 P2P 和实时流媒体 P2P 的区别是什么?</h4>\\n\\n<p>RTMFP 是一个 P2P 系统,但它仅针对实时通信时直接用户到用户之间的通信而设计,不能用于多个对等方之间进行文件共享(使用分段下载)。Facebook 在其 Pipe 应用中使用此协议将大文件直接在两个用户之间传输。</p>\\n\\n<h4 id=\\"rtmfp-和-rtmp-之间的区别是什么\\">RTMFP 和 RTMP 之间的区别是什么?</h4>\\n\\n<p>RTMP 是实时消息协议,RTMFP 代表实时媒体流协议。RTMFP 基于用户数据报协议(UDP),而 RTMP 基于传输控制协议(TCP)。\\n与 RTMP 不同,RTMFP 还支持直接从一个 Adobe Flash Player 传输数据到另一个,而无需经过服务器。</p>\\n\\n<h4 id=\\"flash-player-支持-rtmfp-吗\\">Flash Player 支持 RTMFP 吗?</h4>\\n\\n<p>RTMFP 是基于用户数据报协议(UDP)的,而 RTMP 是基于传输控制协议(TCP)的。与 RTMP 不同,RTMFP 还支持直接在两个 Adobe Flash Player 之间发送数据,而不经过服务器。Flash Player 10.0 仅允许一对一通信进行 P2P,但从 10.1 开始允许应用程序级别的多播。Flash Player 查找适当的分发路由(覆盖网络),并可以将其分发到通过 P2P 连接的组。</p>\\n\\n<h4 id=\\"cumulus-使用-adobe-的-cirrus-key-吗\\">Cumulus 使用 Adobe 的 Cirrus Key 吗?</h4>\\n\\n<p>不!当然,这是Cumulus的主要目标:成为Cirrus GPL的替代品。唯一的限制是:你的CPU,内存和单台机器的端口数。</p>\\n\\n<h4 id=\\"这个开源项目合法吗\\">这个开源项目合法吗?</h4>\\n\\n<p>在美国,数字千年版权法(Digital Millennium Copyright Act)规定,逆向工程用于协议互操作性是合法的。你可以在 WikiPedia 上查看相关讨论:</p>\\n\\n<ul>\\n <li>http://en.wikipedia.org/wiki/Real_Time_Media_Flow_Protocol</li>\\n <li>http://en.wikipedia.org/wiki/Proprietary_protocol</li>\\n <li>http://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act</li>\\n</ul>\\n\\n<p>当逆向工程的目的是协议互操作性时,有法律先例。在美国,数字千年版权法(Digital Millennium Copyright Act)为逆向工程软件以使其与其他软件互操作提供了安全保障。</p>\\n\\n<h3 id=\\"二openrtmfp和cumulus\\">二、OpenRTMFP 和 Cumulus</h3>\\n\\n<p>OpenRTMFP 是一个开源的 RTMFP 实现,可以用于构建基于 RTMFP 的应用程序。它包含了 RTMFP 协议的实现,以及一些额外的功能,如媒体流传输、P2P 通信、脚本引擎和数据存储。</p>\\n\\n<p>Cumulus 是一个基于 OpenRTMFP 的服务器,是一个完整的开源且跨平台的 RTMFP 服务器,可通过脚本进行扩展。CumulusServer 根据 GPL 许可在考虑以下 4 个概念的情况下开发:速度、轻量、跨平台和可扩展。尽管尚未发布版本,但只有在 CumulusServer 经过测试和批准后才会将代码推送到 github。实际上,主要稳定的功能有:</p>\\n\\n<ul>\\n <li>P2P rendez-vous service</li>\\n <li>live streaming</li>\\n <li>RPC、pull、push exchange,实际上客户端和服务器之间的所有 AMF 可能交换</li>\\n <li>脚本引擎,用于创建自己的应用服务器或扩展 Cumulus 的功能</li>\\n <li>可扩展性和负载均衡解决方案</li>\\n</ul>\\n\\n<p>下面的内容是本篇 blog 的重点,包括两部分:先是 OpenRTMFP 应用的核心 CumulusServer 的入门介绍与部署,然后用 Lua 编写 HelloWorld 应用扩展 CumulusServer,我们开始吧!</p>\\n\\n<h3 id=\\"三入门介绍与部署cumulusserver\\">三、入门介绍与部署 CumulusServer</h3>\\n\\n<h4 id=\\"1背景介绍\\">1、背景介绍</h4>\\n\\n<p>OpenRTMFP 可以帮助你实现 Flash 的实时应用的高并发扩展,OpenRTMFP/Cumulus 是基于 GNU General Public License 的。</p>\\n\\n<p>POCO:POrtable COmponents,是一个强大的开源 C++ 库。其在 C++ 开发中的角色,相当于 Java Class Library、苹果的 Cocoa、.NET framework。</p>\\n\\n<h4 id=\\"2准备工作\\">2、准备工作</h4>\\n\\n<p>下载:</p>\\n\\n<table>\\n <thead>\\n <tr>\\n <th><strong>External Dependencies</strong></th>\\n <th><strong>Official Site</strong></th>\\n <th><strong>Windows</strong></th>\\n <th><strong>Linux/OSX</strong></th>\\n </tr>\\n </thead>\\n <tbody>\\n <tr>\\n <td>OpenSSL</td>\\n <td><a href=\\"http://www.slproweb.com/products/Win32OpenSSL.html\\">Official Site</a></td>\\n <td><a href=\\"http://www.slproweb.com/download/Win32OpenSSL_Light-1_0_1.exe\\">Download</a></td>\\n <td><a href=\\"http://www.openssl.org/source/openssl-1.0.1.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>Lua</td>\\n <td><a href=\\"http://www.lua.org/\\">Official Site</a></td>\\n <td><a href=\\"http://luaforwindows.googlecode.com/files/LuaForWindows_v5.1.4-45.exe\\">Download</a></td>\\n <td><a href=\\"http://www.lua.org/ftp/lua-5.1.5.tar.gz\\">Download</a></td>\\n </tr>\\n <tr>\\n <td>POCO</td>\\n <td><a href=\\"http://pocoproject.org/\\">Official Site</a></td>\\n <td><a href=\\"http://downloads.sourceforge.net/project/poco/sources/poco-1.4.3/poco-1.4.3p1.zip\\">Download</a></td>\\n <td><a href=\\"https://sourceforge.net/projects/poco/files/sources/poco-1.4.3/poco-1.4.3p1.tar.gz/download\\">Download</a></td>\\n </tr>\\n </tbody>\\n</table>\\n\\n<p>注意:POCO for linux 版本必须是 1.4.0 或更高,否则会引起 TCP 相关的 bug。</p>\\n\\n<h4 id=\\"3安装\\">3、安装</h4>\\n\\n<h5 id=\\"31外部依赖的安装\\">3.1、外部依赖的安装</h5>\\n\\n<p>Windows 下略,Linux 下基本就是:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span>./configuremakesudo\\n<span class=\\"nv\\">$ </span>make <span class=\\"nb\\">install</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"32安装openrtmfpcumulus\\">3.2、安装 OpenRTMFP/Cumulus</h5>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">cd </span>OpenRTMFP-Cumulus/CumulusLib\\n<span class=\\"nv\\">$ </span>make\\n<span class=\\"nv\\">$ </span><span class=\\"nb\\">cd</span> ../CumulusServer\\n<span class=\\"nv\\">$ </span>make\\n</code></pre></div></div>\\n\\n<p>如果出现了 <code class=\\"language-plaintext highlighter-rouge\\">.h</code> 文件、lib 库找不到的情况,请修改 OpenRTMFP-Cumulus/CumulusLib/Makefile 或 OpenRTMFP-Cumulus/CumulusServer/Makefile。</p>\\n\\n<h4 id=\\"4配置\\">4、配置</h4>\\n\\n<p>通过编写 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP-Cumulus/CumulusServer/CumulusServer.ini</code> 文件来为 OpenRTMFP-Cumulus 进行个性化配置(默认是没有这个文件的),这个文件的内容形如:</p>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1985</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span>\\n<span class=\\"n\\">name</span><span class=\\"o\\">=</span><span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span><span class=\\"o\\">=</span><span class=\\"n\\">C</span><span class=\\"p\\">:</span><span class=\\"o\\">/</span><span class=\\"n\\">CumulusServer</span><span class=\\"o\\">/</span><span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<p>一些字段的设置含义如下,摘自:<a href=\\"https://github.com/OpenRTMFP/Cumulus/wiki/Installation\\">地址</a>。</p>\\n\\n<ul>\\n <li>公开给 Client 的端口号 <code class=\\"language-plaintext highlighter-rouge\\">port</code>,默认值是 1935(RTMFP 服务器的默认端口),用于 CumulusServer 监听 RTMFP 请求。</li>\\n <li>UDP 缓冲区字节数 <code class=\\"language-plaintext highlighter-rouge\\">udpBufferSize</code>, allows to change the size in bytes of UDP reception and sending buffer. Increases this value if your operating system has a default value too lower for important loads.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAliveServer</code>, time in seconds for periodically sending packets keep-alive with server, 15s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">keepAlivePeer</code>, time in seconds for periodically sending packets keep-alive between peers, 10s by default (valid value is from 5s to 255s).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.activated</code>, activate or not the edges server on the RTMFP server (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, CumulusServer stays a RTMFP server without edges ability (default value is false).</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.port</code>, port for the edges server, to accept incoming new CumulusEdge instances (see CumulusEdge, Scalability page for more details about CumulusEdge). By default, it’s the port 1936.</li>\\n</ul>\\n\\n<blockquote>\\n <p>Warning: This port will receive plain text request from edges, for this purpose it should not be made public. It’s very important for security consideration. It must be available only for CumulusEdge instances, and anything else.</p>\\n</blockquote>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">edges.attemptsBeforeFallback</code>, number of CumulusEdge attempt connections before falling back to CumulusServer (see CumulusEdge, Scalability page for more details about CumulusEdge). By default the value is 2 (in practical, 2 attempts happens after 5 sec approximately).</li>\\n <li>SMTP IP 地址 <code class=\\"language-plaintext highlighter-rouge\\">smtp.host</code>, configure a SMTP host to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is localhost.</li>\\n <li>SMTP 端口 <code class=\\"language-plaintext highlighter-rouge\\">smtp.port</code>, configure a SMTP port to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 25.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">smtp.timeout</code>, configure a SMTP timeout session in seconds to use mails feature provided by Cumulus in server application (see Server Application, Sockets page for more details about mails feature). By default the value is 60 seconds.</li>\\n <li>日志路径 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code>,默认是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/logsby</code>。</li>\\n <li>日志文件名称 <code class=\\"language-plaintext highlighter-rouge\\">logs.name</code>,默认是<code class=\\"language-plaintext highlighter-rouge\\">log</code>。</li>\\n</ul>\\n\\n<h4 id=\\"5启动\\">5、启动</h4>\\n\\n<p>Windows 下的启动方法为:</p>\\n\\n<pre><code class=\\"language-dos\\">$ CumulusServer.exe /registerService [/displayName=CumulusServer /description=\\"Open Source RTMFP Server\\" /startup=automatic]\\n</code></pre>\\n\\n<p>Unix-like 下的启动方法为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"o\\">[</span><span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>/var/run/CumulusServer.pid]\\n</code></pre></div></div>\\n\\n<p>具体地,我的启动命令为:</p>\\n\\n<div class=\\"language-sh highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nv\\">$ </span><span class=\\"nb\\">sudo</span> ./CumulusServer <span class=\\"nt\\">--daemon</span> <span class=\\"nt\\">--pidfile</span><span class=\\"o\\">=</span>./CumulusServer.pid\\n</code></pre></div></div>\\n\\n<h4 id=\\"6基本使用\\">6、基本使用</h4>\\n\\n<p>本地 Flash client 可以通过如下语句连接:</p>\\n\\n<div class=\\"language-as highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"nx\\">$</span> <span class=\\"kd\\">var</span> <span class=\\"nx\\">nc</span><span class=\\"o\\">:</span><span class=\\"nx\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nx\\">NetConnection</span><span class=\\"p\\">()</span><span class=\\"o\\">;</span><span class=\\"nx\\">nc</span><span class=\\"p\\">.</span><span class=\\"nx\\">connect</span><span class=\\"p\\">(</span><span class=\\"s2\\">\\"rtmfp://localhost/\\"</span><span class=\\"p\\">)</span><span class=\\"o\\">;</span>\\n</code></pre></div></div>\\n\\n<p>RTMFP默认是采用1935端口,如果你特别指定了其他端口,比如12345,请使用如下方式:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>nc.connect(\\"rtmfp://localhost:12345/\\");\\n</code></pre></div></div>\\n\\n<h4 id=\\"7扩展cumulusserverserverapplication\\">7、扩展 CumulusServer(Server Application)</h4>\\n\\n<p>启动CumulusServer后,会在可执行文件的目录下出现一个www目录,该目录的作用,就是作为 Server Application 的默认根目录。具体的对应关系如下:</p>\\n\\n<blockquote>\\n <p>rtmfp://host:port/ -&gt; [CumulusServer folder]/www/main.lua (root application)</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/myApplication -&gt; [CumulusServer folder]/www/myApplication/main.lua</p>\\n</blockquote>\\n\\n<blockquote>\\n <p>rtmfp://host:port/Games/myGame -&gt; [CumulusServer folder]/www/Games/myGame/main.lua</p>\\n</blockquote>\\n\\n<p>另外要提醒的是,如果main.lua文件被修改,则不需要重启 CumulusServer,因为 Server Application 的创建是一种动态的方式。</p>\\n\\n<h3 id=\\"三用lua编写helloworld应用扩展cumulusserver\\">三、用 Lua 编写 HelloWorld 应用扩展 CumulusServer</h3>\\n\\n<p>下面的这个实例是在本地(Client 与 Server 位于同一机器上)测试的。</p>\\n\\n<h4 id=\\"1server-side\\">1、Server-side</h4>\\n\\n<h5 id=\\"11serverconfiguration\\">1.1、Server configuration</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"p\\">;</span> <span class=\\"n\\">CumulusServer</span><span class=\\"p\\">.</span><span class=\\"n\\">ini</span>\\n<span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">1935</span>\\n<span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">114688</span>\\n<span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span>\\n<span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">15</span>\\n<span class=\\"p\\">[</span><span class=\\"n\\">logs</span><span class=\\"p\\">]</span><span class=\\"n\\">name</span> <span class=\\"o\\">=</span> <span class=\\"n\\">log</span>\\n<span class=\\"n\\">directory</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logs</span>\\n</code></pre></div></div>\\n\\n<h5 id=\\"12applicationfile\\">1.2、Application file</h5>\\n\\n<div class=\\"language-lua highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">function</span> <span class=\\"nf\\">onConnection</span><span class=\\"p\\">(</span><span class=\\"n\\">client</span><span class=\\"p\\">,</span><span class=\\"n\\">response</span><span class=\\"p\\">,</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">function</span> <span class=\\"nf\\">client</span><span class=\\"p\\">:</span><span class=\\"n\\">test</span><span class=\\"p\\">(</span><span class=\\"o\\">...</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">name</span><span class=\\"p\\">,</span><span class=\\"n\\">firstname</span> <span class=\\"o\\">=</span> <span class=\\"n\\">unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">arg</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">return</span> <span class=\\"s2\\">\\"Hello \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">firstname</span><span class=\\"o\\">..</span><span class=\\"s2\\">\\" \\"</span><span class=\\"o\\">..</span><span class=\\"n\\">name</span>\\n <span class=\\"k\\">end</span>\\n <span class=\\"k\\">end</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2client-side\\">2、Client-side</h4>\\n\\n<div class=\\"language-java highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"c1\\">// CumulusClient.as</span>\\n\\n<span class=\\"kn\\">package</span> <span class=\\"err\\">{</span>\\n <span class=\\"nn\\">import</span> <span class=\\"n\\">flash</span><span class=\\"o\\">.</span><span class=\\"na\\">display</span><span class=\\"o\\">.</span><span class=\\"na\\">Sprite</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetConnection</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.NetStream</span><span class=\\"o\\">;</span>\\n <span class=\\"kn\\">import</span> <span class=\\"nn\\">flash.net.Responder</span><span class=\\"o\\">;</span>\\n\\n <span class=\\"kd\\">public</span> <span class=\\"kd\\">class</span> <span class=\\"nc\\">CumulusClient</span> <span class=\\"kd\\">extends</span> <span class=\\"nc\\">Sprite</span> <span class=\\"o\\">{</span>\\n <span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">nc:</span><span class=\\"nc\\">NetConnection</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t<span class=\\"kd\\">private</span> <span class=\\"kt\\">var</span> <span class=\\"nl\\">ns:</span><span class=\\"nc\\">NetStream</span> <span class=\\"o\\">=</span> <span class=\\"kc\\">null</span><span class=\\"o\\">;</span>\\n \\t\\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">CumulusClient</span><span class=\\"o\\">()</span> <span class=\\"o\\">{</span>\\n <span class=\\"n\\">nc</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nc\\">NetConnection</span><span class=\\"o\\">();</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">connect</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"rtmfp://localhost\\"</span><span class=\\"o\\">);</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">client</span> <span class=\\"o\\">=</span> <span class=\\"k\\">this</span><span class=\\"o\\">;</span>\\n <span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">call</span><span class=\\"o\\">(</span><span class=\\"s\\">\\"test\\"</span><span class=\\"o\\">,</span><span class=\\"k\\">new</span> <span class=\\"nc\\">Responder</span><span class=\\"o\\">(</span><span class=\\"n\\">onResult</span><span class=\\"o\\">,</span><span class=\\"n\\">onStatus</span><span class=\\"o\\">),</span> <span class=\\"s\\">\\"OpenRTMFP/Cumulus\\"</span><span class=\\"o\\">,</span> <span class=\\"s\\">\\"World\\"</span><span class=\\"o\\">)</span>\\n <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">close</span><span class=\\"o\\">():</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span> \\n\\t\\t\\t<span class=\\"n\\">nc</span><span class=\\"o\\">.</span><span class=\\"na\\">close</span><span class=\\"o\\">();</span>\\n \\t<span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onStatus</span><span class=\\"o\\">(</span><span class=\\"nl\\">status:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">status</span><span class=\\"o\\">.</span><span class=\\"na\\">description</span><span class=\\"o\\">)</span>\\n\\t <span class=\\"o\\">}</span>\\n \\n \\t<span class=\\"kd\\">public</span> <span class=\\"n\\">function</span> <span class=\\"nf\\">onResult</span><span class=\\"o\\">(</span><span class=\\"nl\\">response:</span><span class=\\"nc\\">Object</span><span class=\\"o\\">):</span><span class=\\"kt\\">void</span> <span class=\\"o\\">{</span>\\n \\t<span class=\\"n\\">trace</span><span class=\\"o\\">(</span><span class=\\"n\\">response</span><span class=\\"o\\">)</span> <span class=\\"c1\\">// expected to display \\"Hello World OpenRTMFP/Cumulus\\" </span>\\n\\t <span class=\\"o\\">}</span> \\n\\t<span class=\\"o\\">}</span>\\n<span class=\\"o\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3运行结果\\">3、运行结果</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Hello World OpenRTMFP/Cumulus\\n[SWF] CumulusClient.swf - 解压缩后为 1,776 个字节\\n[卸装 SWF] CumulusClient.swf\\n</code></pre></div></div>\\n\\n<h4 id=\\"4远程测试一个免费的测试服务器\\">4、远程测试:一个免费的测试服务器</h4>\\n\\n<p>获取 Developer Key 的地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/index.jsp\\n</code></pre></div></div>\\n\\n<p>服务器配置信息:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>Server: amd64 OS: Linux 2.6.18-028stab095.1\\nServer IP: 108.59.252.39\\nOpenRTMFP as of: 22.Feb.2012\\n</code></pre></div></div>\\n\\n<p>编写服务器段应用地址:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>http://108.59.252.39:8080/CumulusServer/manage_ssls.jsp\\n</code></pre></div></div>\\n\\n<p>快去试试吧 :)</p>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的第二篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 2:CumulusServer 源码启动流程分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-14T11:20:46+00:00\\" class=\\"by-line\\">14 Apr 2012, 广州 | 作者 麦克船长 | 总计 18890 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一cumulus-启动源码分析\\" id=\\"markdown-toc-一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数\\" id=\\"markdown-toc-1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</a></li>\\n <li><a href=\\"#2maincpp-中的-cumulusserver-构造函数\\" id=\\"markdown-toc-2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</a></li>\\n <li><a href=\\"#3maincpp-中的-cumulusserver-的-initialize-成员函数\\" id=\\"markdown-toc-3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</a></li>\\n <li><a href=\\"#4maincpp-中的-cumulusserver-的-main-成员函数\\" id=\\"markdown-toc-4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</a></li>\\n <li><a href=\\"#5cumulusserver-是-serverapplication-的子类\\" id=\\"markdown-toc-5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</a></li>\\n <li><a href=\\"#6serverapplication-是-application-的子类\\" id=\\"markdown-toc-6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</a></li>\\n <li><a href=\\"#7反初始化\\" id=\\"markdown-toc-7反初始化\\">7、反初始化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#二cumulusserver-各项交互功能的源码解读\\" id=\\"markdown-toc-二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</a> <ul>\\n <li><a href=\\"#1命令行选项设定\\" id=\\"markdown-toc-1命令行选项设定\\">1、命令行选项设定</a></li>\\n <li><a href=\\"#2处理命令行选项\\" id=\\"markdown-toc-2处理命令行选项\\">2、处理命令行选项</a></li>\\n <li><a href=\\"#6dump-logs\\" id=\\"markdown-toc-6dump-logs\\">6、Dump logs</a></li>\\n <li><a href=\\"#3停止运行\\" id=\\"markdown-toc-3停止运行\\">3、停止运行</a></li>\\n <li><a href=\\"#4载入配置\\" id=\\"markdown-toc-4载入配置\\">4、载入配置</a></li>\\n <li><a href=\\"#5处理日志\\" id=\\"markdown-toc-5处理日志\\">5、处理日志</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三maincpp-的-main-函数源码分析\\" id=\\"markdown-toc-三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</a> <ul>\\n <li><a href=\\"#1maincpp-中的-main-函数中的-server\\" id=\\"markdown-toc-1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></a></li>\\n <li><a href=\\"#2maincpp-中-main-函数的-serverstart\\" id=\\"markdown-toc-2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></a></li>\\n <li><a href=\\"#3回顾一下整个启动流程\\" id=\\"markdown-toc-3回顾一下整个启动流程\\">3、回顾一下整个启动流程</a></li>\\n <li><a href=\\"#4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\" id=\\"markdown-toc-4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文对 CumulusServer 的启动流程进行了详细的源码解读,其中还包括 CumulusServer 如何处理命令行的各个输入选项、各项命令、如何 dump logs、载入配置、处理日志。首先要知道的是,OpenRTMFP/Cumulus 中使用到的库有 Poco、OpenSSL 和 Lua。</p>\\n\\n<h3 id=\\"一cumulus-启动源码分析\\">一、Cumulus 启动源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数</h4>\\n\\n<p>入口在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"kt\\">int</span> <span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">argv</span><span class=\\"p\\">[])</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先检查内存泄露,不过目前这个开发中的项目还没有实现这个功能,只是个空函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">DetectMemoryLeak</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后会创建一个匿名的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象,并调用其 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,该函数由 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 从 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 中继承而来,而 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 由是从 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 继承而来的。<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 对象调用 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,实际是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是调用 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的函数,而该 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数则是先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。如果 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数被调用时抛出异常,则不会执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,但仍然会执行 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Runs the application by performing additional initializations</span>\\n <span class=\\"c1\\">// and calling the main() method.</span>\\n <span class=\\"k\\">return</span> <span class=\\"nf\\">CumulusServer</span><span class=\\"p\\">().</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">argc</span><span class=\\"p\\">,</span> <span class=\\"n\\">argv</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2maincpp-中的-cumulusserver-构造函数\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer()</code> 构造函数</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">CumulusServer</span><span class=\\"p\\">()</span><span class=\\"o\\">:</span> <span class=\\"n\\">_helpRequested</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span> <span class=\\"c1\\">// 显示帮助信息</span>\\n <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_middle</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_isInteractive</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pLogFile</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3maincpp-中的-cumulusserver-的-initialize-成员函数\\">3、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 成员函数</h4>\\n\\n<p>在执行 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数之前,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 会启动 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数,传入的参数就是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 自己,可以猜到 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run</code> 方法中,调用该函数时的参数是 <code class=\\"language-plaintext highlighter-rouge\\">this</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">Application</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">self</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>调用父函数 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的初始化函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">initialize</span><span class=\\"p\\">(</span><span class=\\"n\\">self</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>再继续下面的源码分析之前,先要知道,根据 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> ,<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 类有一些内置的配置属性,如下:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.path</code>: 可执行文件的绝对路径;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.name</code>: 可执行文件的文件名(含扩展名);</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.baseName</code>: 可执行文件的文件名(不含扩展名)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.dir</code>: 可执行文件的所在目录;</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">application.configDir</code>: 配置文件所在目录;</li>\\n</ul>\\n\\n<p>所以下面就读取了可执行文件的所在目录,其中第二个参数表示默认值(即当前目录):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>然后读取配置文件,目录为上一句所得到的 <code class=\\"language-plaintext highlighter-rouge\\">dir</code>,文件名(不含扩展名)为 <code class=\\"language-plaintext highlighter-rouge\\">application.basename</code> 内置配置属性值,其默认值为 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>,然后加上点和扩展名 <code class=\\"language-plaintext highlighter-rouge\\">.ini</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">dir</span>\\n <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.baseName\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"CumulusServer\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">+</span> <span class=\\"s\\">\\".ini\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这样就加载完配置了。然后查看当前的进程是从命令行运行的(命令行是交互的,所以是 interactive),还是以 daemon 方式运行的,这个函数是ServerApplication的一个成员函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_isInteractive</span> <span class=\\"o\\">=</span> <span class=\\"n\\">isInteractive</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>然后获取表示日志文件所在目录的字符串,其中 <code class=\\"language-plaintext highlighter-rouge\\">logs.directory</code> 是外置配置属性(配置文件中),其默认值为上面获取到的可执行文件路径(一般为当前路径)与 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 的组合,即一般为当前目录下的 <code class=\\"language-plaintext highlighter-rouge\\">logs</code> 目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">string</span> <span class=\\"nf\\">logDir</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.directory\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">dir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"logs\\"</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>创建日志文件目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">logDir</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n\\n</code></pre></div></div>\\n\\n<p>日志文件绝对路径,<code class=\\"language-plaintext highlighter-rouge\\">logs</code> 为默认的日志文件名(不含扩展名的部分),扩展名用0:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">logDir</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span> <span class=\\"o\\">+</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"logs.name\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\".\\"</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pLogFile</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"nf\\">File</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"0\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>用日志流打开日志文件(方式为追加写入):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Logs</code> 是一个方法类(其中的 <code class=\\"language-plaintext highlighter-rouge\\">public</code> 函数都是静态的),<code class=\\"language-plaintext highlighter-rouge\\">SetLogger</code> 的作用就是将Logs中的似有静态成员设置为某个 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code> 对象(由于 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Logger</code>)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLogger</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4maincpp-中的-cumulusserver-的-main-成员函数\\">4、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 成员函数</h4>\\n\\n<p>OpenRTMFP/Cumulus 是一个基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::Application</code> 的服务端应用(准确的说是基于 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::ServerApplication</code> 的服务端应用)。如果没有特殊的启动要求,可以调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco/Application.h</code> 中定义的宏 <code class=\\"language-plaintext highlighter-rouge\\">POCO_APP_MAIN</code> 来完成初始化、日志和启动(该宏会根据不同的平台启用不同的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数)。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数在调用完 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> 函数后,会调用 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数,该 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的定义在 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">int</span> <span class=\\"nf\\">main</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">vector</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&gt;&amp;</span> <span class=\\"n\\">args</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>首先看是否是要求帮助信息,<code class=\\"language-plaintext highlighter-rouge\\">displayHelp</code> 是借助 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::HelpFormatter</code> 实现的,<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的构造函数会在调用时将 <code class=\\"language-plaintext highlighter-rouge\\">_helpRequested</code> 设置为 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_helpRequested</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">displayHelp</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果不是,则进入启动状态,首先创建一个 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServerParams</code> 对象 <code class=\\"language-plaintext highlighter-rouge\\">params</code>,用来存储 OpenRTMFP/CumulusServer 的基本配置信息。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">RTMFPServerParams</span> <span class=\\"n\\">params</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">params</code> 存储 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的端口号和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的端口号:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"port\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RTMFP_DEFAULT_PORT</span><span class=\\"o\\">+</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getBool</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.activated\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">WARN</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"edges.port must have a positive value if \\\\\\n edges.activated is true. Server edges is \\\\\\n disactivated.\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"o\\">=</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">_pCirrus</code> 为 <code class=\\"language-plaintext highlighter-rouge\\">SocketAddress</code> 的成员,是封装IP地址和端口号的对象。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_middle</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>UDB 所使用的缓冲区大小:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"udpBufferSize\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"keepAliveServer\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"keepAlivePeer\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>失败前 CumulusEdge 的尝试次数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span>\\n <span class=\\"s\\">\\"edges.attemptsBeforeFallback\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span><span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> 函数是 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中必须调用的,意为等待终止运行的操作请求。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// wait for CTRL-C or kill</span>\\n <span class=\\"n\\">waitForTerminationRequest</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>一旦接收到终止操作的请求,就会执行下面这句,用以退出 OpenRTMFP/Cumulus 的运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"c1\\">// Stop the server</span>\\n <span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">stop</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">catch</code> 一些可能产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Configuration problem : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>OpenRTMFP/CumulusServer 停止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">return</span> <span class=\\"n\\">Application</span><span class=\\"o\\">::</span><span class=\\"n\\">EXIT_OK</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5cumulusserver-是-serverapplication-的子类\\">5、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的子类</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 对其子类有如下要求:</p>\\n\\n<ul>\\n <li>Subsystems must be registered in the constructor.</li>\\n <li>All non-trivial initializations must be made in the <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>` method.</li>\\n <li>At the end of the <code class=\\"language-plaintext highlighter-rouge\\">main()</code> method, <code class=\\"language-plaintext highlighter-rouge\\">waitForTerminationRequest()</code> should be called.</li>\\n</ul>\\n\\n<h4 id=\\"6serverapplication-是-application-的子类\\">6、<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的子类</h4>\\n\\n<p>Application 对其子类的要求是,如下这些成员函数必须被覆盖:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">initialize()</code> (the one-argument, protected variant):上一篇已介绍过。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code>:下面会介绍,Application 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会在调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize()</code> 函数。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">reinitialize()</code></li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">defineOptions()</code>:定义命令行启动选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">handleOption()</code>:响应相应的命令行选项。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">main()</code></li>\\n</ul>\\n\\n<h4 id=\\"7反初始化\\">7、反初始化</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 的,<code class=\\"language-plaintext highlighter-rouge\\">ServerApplication</code> 是继承 <code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的。<code class=\\"language-plaintext highlighter-rouge\\">Application</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run()</code> 函数会先调用 <code class=\\"language-plaintext highlighter-rouge\\">initialize()</code>,然后调用 <code class=\\"language-plaintext highlighter-rouge\\">main()</code>,最后调用 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code>。最后这个反初始化过程,在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 就是直接调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">uninitialize</code> 函数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">uninitialize</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">uninitialize</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"二cumulusserver-各项交互功能的源码解读\\">二、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 各项交互功能的源码解读</h3>\\n\\n<h4 id=\\"1命令行选项设定\\">1、命令行选项设定</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的命令行选项有:<code class=\\"language-plaintext highlighter-rouge\\">log(l)</code>、<code class=\\"language-plaintext highlighter-rouge\\">dump(d)</code>、<code class=\\"language-plaintext highlighter-rouge\\">cirrus(c)</code>、<code class=\\"language-plaintext highlighter-rouge\\">middle(m)</code>、<code class=\\"language-plaintext highlighter-rouge\\">help(h)</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">OptionSet</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">options</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">defineOptions</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>设定日志级别(0 - 8,默认是 6,表示 info 级别)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"l\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Log level argument, must be beetween 0 and 8 : \\\\\\n nothing, fatal, critic, error, warn, note, info, debug, trace. \\\\\\n Default value is 6 (info), all logs until info level are displayed.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">argument</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"level\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>其他一些选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"d\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Enables packet traces in logs. Optional arguments \\\\\\n are 'middle' or 'all' respectively to displays just middle packet \\\\\\n process or all packet process. If no argument is given, just outside \\\\\\n packet process will be dumped.\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"middle|all\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"c\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Cirrus address to activate a 'man-in-the-middle' \\\\\\n developer mode in bypassing flash packets to the official cirrus \\\\\\n server of your choice, it's a instable mode to help Cumulus developers, \\\\\\n </span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\">p2p.rtmfp.net:10000</span><span class=\\"se\\">\\\\\\"</span><span class=\\"s\\"> for example. By adding the 'dump' argument, \\\\\\n you will able to display Cirrus/Flash packet exchange in your logs \\\\\\n (see 'dump' argument).\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">false</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"address\\"</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"m\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"Enables a 'man-in-the-middle' developer mode \\\\\\n between two peers. It's a instable mode to help Cumulus developers. \\\\\\n By adding the 'dump' argument, you will able to display Flash/Flash \\\\\\n packet exchange in your logs (see 'dump' argument).\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n</code></pre></div></div>\\n\\n<p>显示帮助信息的选项:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">options</span><span class=\\"p\\">.</span><span class=\\"n\\">addOption</span><span class=\\"p\\">(</span>\\n <span class=\\"n\\">Option</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"h\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"Displays help information about command-line usage.\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">required</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">)</span>\\n <span class=\\"p\\">.</span><span class=\\"n\\">repeatable</span><span class=\\"p\\">(</span><span class=\\"nb\\">false</span><span class=\\"p\\">));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">OptionSet</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Util::OptionSet</code>,调用addOption可以向其中增加选项Option。其中required和repeatable表示:</p>\\n\\n<p>Sets whether the option is required (flag == true) or optional (flag == false).\\nReturns true if the option can be specified more than once, or false if at most once.\\n当需要显示帮助信息时,调用如下函数:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">displayHelp</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">HelpFormatter</span> <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">(</span><span class=\\"n\\">options</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setCommand</span><span class=\\"p\\">(</span><span class=\\"n\\">commandName</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setUsage</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"OPTIONS\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">setHeader</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"CumulusServer, open source RTMFP server\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">helpFormatter</span><span class=\\"p\\">.</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">cout</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setCommand()</code>: Sets the command name.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setUsage()</code>: Sets the usage string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">setHeader()</code>: Sets the header string.</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">format()</code>: Writes the formatted help text to the given stream.</li>\\n</ul>\\n\\n<h4 id=\\"2处理命令行选项\\">2、处理命令行选项</h4>\\n\\n<p>参数是选项名和选项值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">handleOption</span><span class=\\"p\\">(</span><span class=\\"n\\">name</span><span class=\\"p\\">,</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是帮助:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"help\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_helpRequested</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是cirrus,即该服务的 IP 和端口号,Poco::URI 中有协议名(Scheme)、IP 地址(Host)、端口号(Port)、查询串(Query)等等。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"cirrus\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">URI</span> <span class=\\"n\\">uri</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"rtmfp://\\"</span><span class=\\"o\\">+</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">SocketAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getHost</span><span class=\\"p\\">(),</span><span class=\\"n\\">uri</span><span class=\\"p\\">.</span><span class=\\"n\\">getPort</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' : the exchange will bypass to '%s'\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Mode 'man in the middle' error : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">message</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是dump日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"dump\\"</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"all\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">ALL</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">MIDDLE</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">else</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetDump</span><span class=\\"p\\">(</span><span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">EXTERNAL</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"middle\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果选项是log,表示设定日志级别:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">name</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"log\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">Logs</span><span class=\\"o\\">::</span><span class=\\"n\\">SetLevel</span><span class=\\"p\\">(</span><span class=\\"n\\">atoi</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">c_str</span><span class=\\"p\\">()));</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"6dump-logs\\">6、Dump logs</h4>\\n\\n<p>先加一个作用域锁,然后再向日志流写数据。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">dumpHandler</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">cout</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">write</span><span class=\\"p\\">((</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">data</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>调用 manageLogFile,主要做一些日志大小超出限制的处理。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">manageLogFile</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>先判断是否超过日志文件的大小上线,LOG_SIZE是1000000字节(即约 1 MB)。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">getSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"n\\">LOG_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">num</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">10</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>打开新日志文件:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span> <span class=\\"nf\\">file</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"10\\"</span><span class=\\"p\\">);</span>\\n\\n</code></pre></div></div>\\n\\n<p>如果该文件已经存在,则先删除:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">remove</span><span class=\\"p\\">();</span>\\n\\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">--</span><span class=\\"n\\">num</span> <span class=\\"o\\">&gt;=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">exists</span><span class=\\"p\\">())</span>\\n <span class=\\"n\\">file</span><span class=\\"p\\">.</span><span class=\\"n\\">renameTo</span><span class=\\"p\\">(</span><span class=\\"n\\">_logPath</span> <span class=\\"o\\">+</span> <span class=\\"n\\">NumberFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">num</span> <span class=\\"o\\">+</span> <span class=\\"mi\\">1</span><span class=\\"p\\">));</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">open</span><span class=\\"p\\">(</span><span class=\\"n\\">_pLogFile</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">path</span><span class=\\"p\\">(),</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">in</span> <span class=\\"o\\">|</span> <span class=\\"n\\">ios</span><span class=\\"o\\">::</span><span class=\\"n\\">ate</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span> \\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3停止运行\\">3、停止运行</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 继承了 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code>,该类中有纯虚函数 <code class=\\"language-plaintext highlighter-rouge\\">kill()</code> 需要被实现,于是有:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">kill</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">terminate</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller</code> 的定义在 <code class=\\"language-plaintext highlighter-rouge\\">ApplicationKiller.h</code> 中,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">ApplicationKiller</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">ApplicationKiller</span><span class=\\"p\\">(){}</span>\\n \\n <span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">kill</span><span class=\\"p\\">()</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"4载入配置\\">4、载入配置</h4>\\n\\n<p>在initialize()函数中调用,上一篇已提到过。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">path</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ServerApplication</span><span class=\\"o\\">::</span><span class=\\"n\\">loadConfiguration</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"5处理日志\\">5、处理日志</h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"nf\\">logHandler</span><span class=\\"p\\">(</span><span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">TID</span> <span class=\\"n\\">threadId</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">threadName</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Priority</span> <span class=\\"n\\">priority</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">filePath</span><span class=\\"p\\">,</span>\\n <span class=\\"kt\\">long</span> <span class=\\"n\\">line</span><span class=\\"p\\">,</span> \\n <span class=\\"k\\">const</span> <span class=\\"kt\\">char</span> <span class=\\"o\\">*</span><span class=\\"n\\">text</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>作用域锁:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_logMutex</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">Path</span> <span class=\\"nf\\">path</span><span class=\\"p\\">(</span><span class=\\"n\\">filePath</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">string</span> <span class=\\"n\\">file</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getExtension</span><span class=\\"p\\">()</span> <span class=\\"o\\">==</span> <span class=\\"s\\">\\"lua\\"</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">file</span> <span class=\\"o\\">+=</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">directory</span><span class=\\"p\\">(</span><span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">depth</span><span class=\\"p\\">()</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"/\\"</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果是命令行交互模式(即不是 daemon 模式):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_isInteractive</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">printf</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"%s %s[%ld] %s</span><span class=\\"se\\">\\\\n</span><span class=\\"s\\">\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span> <span class=\\"o\\">-</span> <span class=\\"mi\\">1</span><span class=\\"p\\">],</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getBaseName</span><span class=\\"p\\">()).</span><span class=\\"n\\">c_str</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">line</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">text</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>向日志流输出一句日志:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_logStream</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">DateTimeFormatter</span><span class=\\"o\\">::</span><span class=\\"n\\">format</span><span class=\\"p\\">(</span><span class=\\"n\\">LocalDateTime</span><span class=\\"p\\">(),</span><span class=\\"s\\">\\"%d/%m %H:%M:%S.%c \\"</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">g_logPriorities</span><span class=\\"p\\">[</span><span class=\\"n\\">priority</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">]</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'\\\\t'</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadName</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'('</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">threadId</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\")</span><span class=\\"se\\">\\\\t</span><span class=\\"s\\">\\"</span>\\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"p\\">(</span><span class=\\"n\\">file</span> <span class=\\"o\\">+</span> <span class=\\"n\\">path</span><span class=\\"p\\">.</span><span class=\\"n\\">getFileName</span><span class=\\"p\\">())</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"sc\\">'['</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">line</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"s\\">\\"] \\"</span> \\n <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">text</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">endl</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_logStream</span><span class=\\"p\\">.</span><span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>日志文件的善后处理(主要处理文件大小限制可能产生的问题):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">manageLogFile</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三maincpp-的-main-函数源码分析\\">三、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数源码分析</h3>\\n\\n<h4 id=\\"1maincpp-中的-main-函数中的-server\\">1、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中的 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数中的 <code class=\\"language-plaintext highlighter-rouge\\">server</code></h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中真正启动的是 <code class=\\"language-plaintext highlighter-rouge\\">server</code>,它继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code>,而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::RTMFPServer</code> 又继承自 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Gateway</code>、<code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Handler</code>。而 <code class=\\"language-plaintext highlighter-rouge\\">Cumulus::Startable</code> 继承自 <code class=\\"language-plaintext highlighter-rouge\\">Poco::Runnable</code>,所以其是一个可以运行的线程。在 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 中,这是主线程。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span> <span class=\\"nf\\">server</span><span class=\\"p\\">(</span><span class=\\"n\\">config</span><span class=\\"p\\">().</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"application.dir\\"</span><span class=\\"p\\">,</span> <span class=\\"s\\">\\"./\\"</span><span class=\\"p\\">),</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">,</span> <span class=\\"n\\">config</span><span class=\\"p\\">());</span>\\n<span class=\\"n\\">server</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>这是 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer/Server.h</code> 中定义的,其构造函数的原型为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>个参数含义如下:</p>\\n\\n<blockquote>\\n <p>The Path Root for the Server Application Killer for Termanting the Server Application Server Configuration</p>\\n</blockquote>\\n\\n<p>距离来说,在我的 Worksapce 中:</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">root</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 构造函数的初始化列表极长:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">Server</span><span class=\\"o\\">::</span><span class=\\"n\\">Server</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">root</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ApplicationKiller</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">applicationKiller</span><span class=\\"p\\">,</span>\\n <span class=\\"k\\">const</span> <span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">AbstractConfiguration</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">configurations</span><span class=\\"p\\">)</span> \\n <span class=\\"o\\">:</span> <span class=\\"n\\">_blacklist</span><span class=\\"p\\">(</span><span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"blacklist\\"</span><span class=\\"p\\">,</span> <span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_applicationKiller</span><span class=\\"p\\">(</span><span class=\\"n\\">applicationKiller</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_hasOnRealTime</span><span class=\\"p\\">(</span><span class=\\"nb\\">true</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">_pService</span><span class=\\"p\\">(</span><span class=\\"nb\\">NULL</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">luaMail</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"o\\">=</span><span class=\\"n\\">Script</span><span class=\\"o\\">::</span><span class=\\"n\\">CreateState</span><span class=\\"p\\">(),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getString</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.host\\"</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"localhost\\"</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">SMTPSession</span><span class=\\"o\\">::</span><span class=\\"n\\">SMTP_PORT</span><span class=\\"p\\">),</span>\\n <span class=\\"n\\">configurations</span><span class=\\"p\\">.</span><span class=\\"n\\">getInt</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"smtp.timeout\\"</span><span class=\\"p\\">,</span><span class=\\"mi\\">60</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>下面调用 <code class=\\"language-plaintext highlighter-rouge\\">Poco::File</code> 创建目录:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">File</span><span class=\\"p\\">((</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">WWWPath</span> <span class=\\"o\\">=</span> <span class=\\"n\\">root</span> <span class=\\"o\\">+</span> <span class=\\"s\\">\\"www\\"</span><span class=\\"p\\">).</span><span class=\\"n\\">createDirectory</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>因为 <code class=\\"language-plaintext highlighter-rouge\\">roor</code> 是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/</code> 目录,所以 <code class=\\"language-plaintext highlighter-rouge\\">WWWPath</code> 就是 <code class=\\"language-plaintext highlighter-rouge\\">/Users/michael/Development/workspace/eclipse/OpenRTMFP-Cumulus/Debug/www</code> 目录。然后初始化 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code>,这个 <code class=\\"language-plaintext highlighter-rouge\\">GlobalTable</code> 是和 Lua 有关的东东,这里暂不细说,先知道与 Lua 相关就好。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Service</span><span class=\\"o\\">::</span><span class=\\"n\\">InitGlobalTable</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>下面就涉及到了 Lua script 了:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SCRIPT_BEGIN</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">SCRIPT_CREATE_PERSISTENT_OBJECT</span><span class=\\"p\\">(</span><span class=\\"n\\">Invoker</span><span class=\\"p\\">,</span><span class=\\"n\\">LUAInvoker</span><span class=\\"p\\">,</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">readNextConfig</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"n\\">configurations</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">lua_setglobal</span><span class=\\"p\\">(</span><span class=\\"n\\">_pState</span><span class=\\"p\\">,</span><span class=\\"s\\">\\"cumulus.configs\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">SCRIPT_END</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN</code>、<code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_CREATE_PERSISTENT_OBJECT和SCRIPT_END</code> 都是宏,其定义在 <code class=\\"language-plaintext highlighter-rouge\\">Script.h</code> 文件中,如下:</p>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define SCRIPT_BEGIN(STATE) \\\\\\n if (lua_State* __pState = STATE) { \\\\\\n const char* __error=NULL;\\n \\n#define SCRIPT_CREATE_PERSISTENT_OBJECT(TYPE,LUATYPE,OBJ) \\\\\\n Script::WritePersistentObject&lt;TYPE,LUATYPE&gt;(__pState,OBJ); \\\\\\n lua_pop(__pState,1);\\n \\n#define SCRIPT_END }\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">SCRIPT_BEGIN和SCRIPT_END</code> 经常用到,当与 Lua 相关的操作出现时,都会以这两个宏作为开头和结尾。</p>\\n\\n<h4 id=\\"2maincpp-中-main-函数的-serverstart\\">2、<code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 中 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数的 <code class=\\"language-plaintext highlighter-rouge\\">server.start()</code></h4>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFPServerParams</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">params</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>如果 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusServer</code> 正在运行,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer server is yet running, call stop method before\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定端口号,如果端口号为 0,则返回并终止启动。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_port</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">port</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must have a positive value\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>设定 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusEdge</code> 的端口号,如果其端口号与 <code class=\\"language-plaintext highlighter-rouge\\">OpenRTMFP/CumulusSever</code> 端口号相同,则返回并终止启动:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesPort</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_port</span> <span class=\\"o\\">==</span> <span class=\\"n\\">_edgesPort</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer port must different than RTMFPServer edges.port\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>Cirrus:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">2000000</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// 2 sec by default</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"k\\">new</span> <span class=\\"n\\">Target</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">pCirrus</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_freqManage</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// no waiting, direct process in the middle case!</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode with server %s \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">_pCirrus</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">address</span><span class=\\"p\\">.</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>middle:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_middle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">middle</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer started in man-in-the-middle mode between peers \\\\\\n (unstable debug mode)\\"</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>UDP Buffer:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">udpBufferSize</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"o\\">==</span><span class=\\"mi\\">0</span> <span class=\\"o\\">?</span> \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">getReceiveBufferSize</span><span class=\\"p\\">()</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setReceiveBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">setSendBufferSize</span><span class=\\"p\\">(</span><span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Socket buffer receving/sending size = %u/%u\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">udpBufferSize</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAliveServer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">=</span> \\n <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">5</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">5000</span> <span class=\\"o\\">:</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">keepAlivePeer</span> <span class=\\"o\\">*</span> <span class=\\"mi\\">1000</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">&amp;</span><span class=\\"p\\">)</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span> <span class=\\"o\\">=</span> <span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">edgesAttemptsBeforeFallback</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">setPriority</span><span class=\\"p\\">(</span><span class=\\"n\\">params</span><span class=\\"p\\">.</span><span class=\\"n\\">threadPriority</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>启动线程,进入循环运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">();</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>上句具体的源码实现为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">start</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">running</span><span class=\\"p\\">())</span>\\n <span class=\\"k\\">return</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>如果在运行则返回并终止启动。然后加一个局部锁。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">ScopedLock</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">FastMutex</span><span class=\\"o\\">&gt;</span> <span class=\\"n\\">lock</span><span class=\\"p\\">(</span><span class=\\"n\\">_mutex</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果不得不join()到主线程中,那就join()吧</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_haveToJoin</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">join</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_haveToJoin</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>然后就运行这个线程吧:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">_terminate</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kr\\">_thread</span><span class=\\"p\\">.</span><span class=\\"n\\">start</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_haveToJoin</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"3回顾一下整个启动流程\\">3、回顾一下整个启动流程</h4>\\n\\n<p>到此我们先回顾一下启动过程:</p>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">main.cpp</code> 的启动入口 <code class=\\"language-plaintext highlighter-rouge\\">main()</code> 函数开始,创建 <code class=\\"language-plaintext highlighter-rouge\\">Server</code> 对象并启动(调用 <code class=\\"language-plaintext highlighter-rouge\\">start()</code> 函数)。<code class=\\"language-plaintext highlighter-rouge\\">Server::start()</code> 中调用其父类(<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code>)的父类(<code class=\\"language-plaintext highlighter-rouge\\">Startable</code>)的方法 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 开启线程。\\n调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::start()</code> 函数后,开启线城时传入的参数为 <code class=\\"language-plaintext highlighter-rouge\\">*this</code>,所以就会运行 <code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code>;</p>\\n\\n<h4 id=\\"4rtmfpserverprerunstartableprerun-和-rtmfpserverrun-函数源码\\">4、<code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>、<code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(...)</code> 函数源码</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::run()</code> 调用 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,但这个函数被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖,所以会运行 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::prerun()</code>,其源码如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>如果CumulusEdge:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP edges server starts on %u port\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">onStart</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>运行线程:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<p>处理异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(</span><span class=\\"n\\">exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">what</span><span class=\\"p\\">());</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span> <span class=\\"p\\">(...)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">FATAL</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFPServer unknown error\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>如果跳出了,则终止运行:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">onStop</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">NOTE</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"RTMFP server stops\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<p>该函数内部又会调用父类的 <code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 函数,该函数调用:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">virtual</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<p>它是一个纯虚函数,由 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 实现。</p>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">Startable::prerun()</code> 会调用 <code class=\\"language-plaintext highlighter-rouge\\">void run(const volatile bool&amp; terminate)</code> 方法,该方法被 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖了。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">bool</span> <span class=\\"n\\">Startable</span><span class=\\"o\\">::</span><span class=\\"n\\">prerun</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">return</span> <span class=\\"o\\">!</span><span class=\\"n\\">_terminate</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer</code> 覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">...</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</title>\\n \\t<meta name=\\"description\\" content=\\"CumulusServer 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 CumulusEdge 和 CumulusServer 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 CumulusServer 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 3:CumulusServer 源码主进程主循环分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-15T14:26:58+00:00\\" class=\\"by-line\\">15 Apr 2012, 广州 | 作者 麦克船长 | 总计 3844 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#1绑定地址\\" id=\\"markdown-toc-1绑定地址\\">1、绑定地址</a></li>\\n <li><a href=\\"#2cumulusserver-接收数据\\" id=\\"markdown-toc-2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</a></li>\\n <li><a href=\\"#3如果-cumulusedge-端口存在且-edge-socket-可用\\" id=\\"markdown-toc-3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</a></li>\\n <li><a href=\\"#4cumulusserver-和-cumulusedge-的-socket-都没有数据\\" id=\\"markdown-toc-4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</a></li>\\n <li><a href=\\"#5发送方的-ip-被禁\\" id=\\"markdown-toc-5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</a></li>\\n <li><a href=\\"#6数据包长度小于可能的最小值12\\" id=\\"markdown-toc-6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</a></li>\\n</ul>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 主进程的主循环分析,看本文一篇就够了。从绑定地址开始,本文介绍了如何接收数据,如何在 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 socket 不同情况下的处理逻辑,如何处理发送方 IP 被禁、数据包大小异常等问题。通过本文让你了解 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的主循环,需要你对 POCO 库有一点了解,还要稍微熟悉 C++ 的基本语法。</p>\\n\\n<p>本所要介绍的这个主循环在 <code class=\\"language-plaintext highlighter-rouge\\">RTMFPServer::run(const volatile bool&amp; terminate)</code> 函数中。RTMFPServer覆盖 <code class=\\"language-plaintext highlighter-rouge\\">Startable</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">run(const volatile bool &amp;terminate)</code> 方法。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">RTMFPServer</span><span class=\\"o\\">::</span><span class=\\"n\\">run</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"k\\">volatile</span> <span class=\\"kt\\">bool</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"1绑定地址\\">1、绑定地址</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">address</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_port</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n<span class=\\"err\\">绑定</span><span class=\\"n\\">CumulusEdge</span><span class=\\"err\\">的</span> <span class=\\"n\\">IP</span> <span class=\\"err\\">地址和端口:</span>\\n\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"nf\\">edgesAddress</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"0.0.0.0\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">_edgesPort</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>发送者(Client)的 IP 地址和端口:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">SocketAddress</span> <span class=\\"n\\">sender</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">buff</span><span class=\\"p\\">[</span><span class=\\"n\\">PACKETRECV_SIZE</span><span class=\\"p\\">];</span>\\n <span class=\\"kt\\">int</span> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">terminate</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n \\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">stop</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">idle</span> <span class=\\"o\\">=</span> <span class=\\"n\\">realTime</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">stop</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">break</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span><span class=\\"o\\">=</span><span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"2cumulusserver-接收数据\\">2、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 接收数据</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>从 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 读取:把数据存到 <code class=\\"language-plaintext highlighter-rouge\\">buff</code>,把发送者地址赋给 <code class=\\"language-plaintext highlighter-rouge\\">sender</code>,把所读长度返回给 <code class=\\"language-plaintext highlighter-rouge\\">size</code>。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>处理 <code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 产生的异常:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span><span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"3如果-cumulusedge-端口存在且-edge-socket-可用\\">3、如果 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 端口存在且 edge socket 可用。</h3>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 有数据可读:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"nf\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"o\\">&gt;</span> <span class=\\"mi\\">0</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">try</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">size</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">receiveFrom</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span> <span class=\\"k\\">sizeof</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">),</span> <span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span> <span class=\\"k\\">catch</span><span class=\\"p\\">(</span><span class=\\"n\\">Exception</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ex</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">DEBUG</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Main socket reception : %s\\"</span><span class=\\"p\\">,</span> <span class=\\"n\\">ex</span><span class=\\"p\\">.</span><span class=\\"n\\">displayText</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">bind</span><span class=\\"p\\">(</span><span class=\\"n\\">edgesAddress</span><span class=\\"p\\">,</span> <span class=\\"nb\\">true</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">Edge</span><span class=\\"o\\">*</span> <span class=\\"n\\">pEdge</span> <span class=\\"o\\">=</span> <span class=\\"n\\">edges</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pEdge</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pEdge</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"4cumulusserver-和-cumulusedge-的-socket-都没有数据\\">4、<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code> 都没有数据:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code> 空闲:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">idle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>主线程等待一秒。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">Thread</span><span class=\\"o\\">::</span><span class=\\"n\\">sleep</span><span class=\\"p\\">(</span><span class=\\"mi\\">1</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">isElapsed</span><span class=\\"p\\">(</span><span class=\\"n\\">_freqManage</span><span class=\\"p\\">))</span> <span class=\\"p\\">{</span>\\n</code></pre></div></div>\\n\\n<p>Just middle session</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_middle</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Sessions</span><span class=\\"o\\">::</span><span class=\\"n\\">Iterator</span> <span class=\\"n\\">it</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">for</span> <span class=\\"p\\">(</span><span class=\\"n\\">it</span> <span class=\\"o\\">=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span> <span class=\\"n\\">it</span> <span class=\\"o\\">!=</span> <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">end</span><span class=\\"p\\">();</span> <span class=\\"o\\">++</span><span class=\\"n\\">it</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Middle</span><span class=\\"o\\">*</span> <span class=\\"n\\">pMiddle</span> <span class=\\"o\\">=</span> <span class=\\"k\\">dynamic_cast</span><span class=\\"o\\">&lt;</span><span class=\\"n\\">Middle</span><span class=\\"o\\">*&gt;</span><span class=\\"p\\">(</span><span class=\\"n\\">it</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">second</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">pMiddle</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">pMiddle</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span> <span class=\\"k\\">else</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">_timeLastManage</span><span class=\\"p\\">.</span><span class=\\"n\\">update</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">manage</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"5发送方的-ip-被禁\\">5、发送方的 ip 被禁:</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">isBanned</span><span class=\\"p\\">(</span><span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">()))</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">INFO</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Data rejected because client %s is banned\\"</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">sender</span><span class=\\"p\\">.</span><span class=\\"n\\">host</span><span class=\\"p\\">().</span><span class=\\"n\\">toString</span><span class=\\"p\\">().</span><span class=\\"n\\">c_str</span><span class=\\"p\\">());</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"6数据包长度小于可能的最小值12\\">6、数据包长度小于可能的最小值(12)</h3>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">size</span> <span class=\\"o\\">&lt;</span> <span class=\\"n\\">RTMFP_MIN_PACKET_SIZE</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">ERROR</span><span class=\\"p\\">(</span><span class=\\"s\\">\\"Invalid packet\\"</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"n\\">PacketReader</span> <span class=\\"nf\\">packet</span><span class=\\"p\\">(</span><span class=\\"n\\">buff</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Session</span><span class=\\"o\\">*</span> <span class=\\"n\\">pSession</span> <span class=\\"o\\">=</span> <span class=\\"n\\">findSession</span><span class=\\"p\\">(</span><span class=\\"n\\">RTMFP</span><span class=\\"o\\">::</span><span class=\\"n\\">Unpack</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">));</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"p\\">)</span>\\n <span class=\\"k\\">continue</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"o\\">!</span><span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">checked</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">commitCookie</span><span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"n\\">pSession</span><span class=\\"p\\">);</span>\\n</code></pre></div></div>\\n\\n<p>给 <code class=\\"language-plaintext highlighter-rouge\\">CumulusEdge</code> 或者自己(<code class=\\"language-plaintext highlighter-rouge\\">CumulusServer</code>)的 <code class=\\"language-plaintext highlighter-rouge\\">socket</code>:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">setEndPoint</span><span class=\\"p\\">(</span><span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">isEdges</span> <span class=\\"o\\">?</span> <span class=\\"n\\">_edgesSocket</span> <span class=\\"o\\">:</span> <span class=\\"n\\">_socket</span><span class=\\"p\\">,</span><span class=\\"n\\">sender</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">pSession</span><span class=\\"o\\">-&gt;</span><span class=\\"n\\">receive</span><span class=\\"p\\">(</span><span class=\\"n\\">packet</span><span class=\\"p\\">);</span>\\n <span class=\\"err\\">}</span>\\n <span class=\\"n\\">_handshake</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_sessions</span><span class=\\"p\\">.</span><span class=\\"n\\">clear</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">_socket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span> <span class=\\"p\\">(</span><span class=\\"n\\">_edgesPort</span><span class=\\"o\\">&gt;</span><span class=\\"mi\\">0</span><span class=\\"p\\">)</span>\\n <span class=\\"n\\">_edgesSocket</span><span class=\\"p\\">.</span><span class=\\"n\\">close</span><span class=\\"p\\">();</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">_pCirrus</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">delete</span> <span class=\\"n\\">_pCirrus</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">_pCirrus</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">NULL</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n<span class=\\"err\\">}</span>\\n</code></pre></div></div>\\n\\n\\t</div>\\n</article>\\n\\n\\n\\n\\t </main>\\n\\t\\t\\n\\t\\t <!-- Pagination links -->\\n \\n\\n\\t </div>\\n\\t \\n\\t <!-- Footer -->\\n\\t <footer><span>@2022 - MikeCaptain.com</span></footer>\\n\\n\\n\\t <!-- Script -->\\n <script src=\\"/pages/Poechant/js/main.js\\"></script>\\t\\n\\n\\n\\t</div>\\n</body>\\n</html>\\n","<!DOCTYPE html>\\n<html>\\n\\n<head>\\n\\t<!-- Meta -->\\n\\t<meta charset=\\"UTF-8\\"/>\\n\\t<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0, maximum-scale=1\\">\\n\\t<meta name=\\"generator\\" content=\\"Jekyll\\">\\n\\n\\t<title>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</title>\\n \\t<meta name=\\"description\\" content=\\"本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。\\">\\n\\n\\t<!-- CSS & fonts -->\\n\\t<link rel=\\"stylesheet\\" href=\\"/pages/Poechant/css/main.css\\">\\n\\n\\t<!-- RSS -->\\n\\t<link href=\\"/atom.xml\\" type=\\"application/atom+xml\\" rel=\\"alternate\\" title=\\"ATOM Feed\\" />\\n\\n \\t<!-- Favicon -->\\n \\t <link rel=\\"shortcut icon\\" type=\\"image/png\\" href=\\"/img/favicon.png\\">\\n\\n \\t <!-- Syntax highlighter -->\\n \\t<link rel=\\"stylesheet\\" href=\\"/css/syntax.css\\" />\\n\\n \\t<!--KaTeX-->\\n \\t<link rel=\\"stylesheet\\" href=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\\" integrity=\\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\\" crossorigin=\\"anonymous\\">\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js\\" integrity=\\"sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script defer src=\\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js\\" integrity=\\"sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa\\" crossorigin=\\"anonymous\\"></script>\\n \\t<script>\\n \\t\\tdocument.addEventListener(\\"DOMContentLoaded\\", function() {\\n \\t\\t\\trenderMathInElement(document.body, {\\n \\t\\t\\t\\t// ...options...\\n \\t\\t\\t});\\n \\t\\t});\\n \\t</script>\\n\\n \\t\\n\\n</head>\\n\\n<body>\\n\\t<div id=\\"wrap\\">\\n\\t \\t\\n\\t \\t<!-- Navigation -->\\n\\t \\t<nav id=\\"nav\\">\\n\\t<div id=\\"nav-list\\">\\n\\t\\t<a href=\\"/pages/Poechant/\\">Home</a>\\n\\n\\t\\t<!-- Nav pages -->\\n\\t <!-- \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n\\t \\n\\t \\n\\t \\n\\t <a href=\\"/pages/Poechant/categories/\\" title=\\"Categories\\">Categories</a>\\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t \\n\\t -->\\n\\n\\t <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\t \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n\\t <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n\\t</div>\\n \\n <!-- Nav footer -->\\n\\t\\n\\t <footer>\\n\\t\\n\\t<span>version 1.0.0</span>\\n\\n</footer>\\n\\t\\n\\n</nav>\\n\\n \\n <!-- Icon menu -->\\n\\t <a id=\\"nav-menu\\">\\n\\t \\t<div id=\\"menu\\"></div>\\n\\t </a>\\n\\n <!-- Header -->\\n \\n <header id=\\"header\\" class=\\"parent justify-spaceBetween\\">\\n <div class=\\"inner w100 relative\\">\\n <span class=\\"f-left\\"> \\n <a href=\\"/pages/Poechant/\\">\\n <h1>\\n <span>Mike</span>Captain\\n </h1>\\n </a>\\n </span>\\n <span id=\\"nav-links\\" class=\\"absolute right bottom\\">\\n\\n <!-- Tech category pages -->\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/ai\\" title=\\"人工智能\\">人工智能</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/rt_tech\\" title=\\"实时技术\\">实时技术</a>\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/web\\" title=\\"前端技术\\">前端技术</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n<!-- Non-tech category pages -->\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n <a href=\\"/pages/Poechant/category/thinking\\" title=\\"思考与生活\\">思考与生活</a>\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n &nbsp;&nbsp;&nbsp;丨&nbsp;\\n\\n <!-- Nav pages -->\\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/about/\\" title=\\"关于我\\">关于我</a>\\n \\n \\n \\n \\n \\n <a href=\\"/pages/Poechant/booklist/\\" title=\\"我的书单\\">我的书单</a>\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n <!-- Nav links -->\\n <!-- <a href=\\"https://github.com/thereviewindex/monochrome/archive/master.zip\\">Download</a>\\n<a href=\\"https://github.com/thereviewindex/monochrome\\">Project on Github</a> -->\\n\\n </span>\\n </div>\\n</header>\\n\\n\\n\\n\\n \\n\\n <!-- Main content -->\\n\\t <div id=\\"container\\">\\n\\t\\t \\n\\t\\t<main>\\n\\n\\t\\t\\t<article id=\\"post-page\\">\\n\\t<h2>OpenRTMFP/Cumulus 原理、源码及实践 4:AMF 解析源码分析</h2>\\t\\t\\n\\t<time datetime=\\"2012-04-24T02:04:55+00:00\\" class=\\"by-line\\">24 Apr 2012, 广州 | 作者 麦克船长 | 总计 30820 字</time>\\n\\t<div class=\\"content\\">\\n\\t\\t<p><strong>本文目录</strong></p>\\n<ul id=\\"markdown-toc\\">\\n <li><a href=\\"#一amf-数据类型定义\\" id=\\"markdown-toc-一amf-数据类型定义\\">一、AMF 数据类型定义</a> <ul>\\n <li><a href=\\"#1数据类型\\" id=\\"markdown-toc-1数据类型\\">1、数据类型</a></li>\\n <li><a href=\\"#2undefined-type\\" id=\\"markdown-toc-2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</a></li>\\n <li><a href=\\"#3null-type\\" id=\\"markdown-toc-3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</a></li>\\n <li><a href=\\"#4false-type\\" id=\\"markdown-toc-4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</a></li>\\n <li><a href=\\"#5true-type\\" id=\\"markdown-toc-5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</a></li>\\n <li><a href=\\"#6integer-type\\" id=\\"markdown-toc-6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</a></li>\\n <li><a href=\\"#7double-type\\" id=\\"markdown-toc-7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</a></li>\\n <li><a href=\\"#8string-type\\" id=\\"markdown-toc-8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</a></li>\\n <li><a href=\\"#9xmldocument-type\\" id=\\"markdown-toc-9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</a></li>\\n <li><a href=\\"#10date-type\\" id=\\"markdown-toc-10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</a></li>\\n <li><a href=\\"#11array-type\\" id=\\"markdown-toc-11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</a></li>\\n <li><a href=\\"#12object-type\\" id=\\"markdown-toc-12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</a></li>\\n <li><a href=\\"#13xml-type\\" id=\\"markdown-toc-13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</a></li>\\n <li><a href=\\"#14bytearray-type\\" id=\\"markdown-toc-14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</a></li>\\n <li><a href=\\"#15amf3-的使用\\" id=\\"markdown-toc-15amf3-的使用\\">15、AMF3 的使用</a> <ul>\\n <li><a href=\\"#151netconnection-and-amf-3\\" id=\\"markdown-toc-151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</a></li>\\n <li><a href=\\"#152netconnection-in-actionscript-30\\" id=\\"markdown-toc-152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</a></li>\\n <li><a href=\\"#153bytearray-idatainput-and-idataoutput\\" id=\\"markdown-toc-153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#二binaryreaderwriter\\" id=\\"markdown-toc-二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></a> <ul>\\n <li><a href=\\"#1amf3-数据格式基础\\" id=\\"markdown-toc-1amf3-数据格式基础\\">1、AMF3 数据格式基础</a></li>\\n <li><a href=\\"#2序列化\\" id=\\"markdown-toc-2序列化\\">2、序列化</a></li>\\n <li><a href=\\"#3反序列化\\" id=\\"markdown-toc-3反序列化\\">3、反序列化</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#三packetreaderwriter\\" id=\\"markdown-toc-三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></a> <ul>\\n <li><a href=\\"#1packetreader\\" id=\\"markdown-toc-1packetreader\\">1、PacketReader</a> <ul>\\n <li><a href=\\"#11封装-memoryinputstream\\" id=\\"markdown-toc-11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></a></li>\\n <li><a href=\\"#12收缩缓冲区\\" id=\\"markdown-toc-12收缩缓冲区\\">1.2、收缩缓冲区</a></li>\\n <li><a href=\\"#13构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-13构造函数拷贝构造函数和析构函数\\">1.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#2packetwriter\\" id=\\"markdown-toc-2packetwriter\\">2、<code class=\\"language-plaintext highlighter-rouge\\">PacketWriter</code></a> <ul>\\n <li><a href=\\"#21封装memoryoutputstream\\" id=\\"markdown-toc-21封装memoryoutputstream\\">2.1、封装<code class=\\"language-plaintext highlighter-rouge\\">MemoryOutputStream</code></a></li>\\n <li><a href=\\"#22封装-binarywriter\\" id=\\"markdown-toc-22封装-binarywriter\\">2.2、封装 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriter</code></a></li>\\n <li><a href=\\"#23构造函数拷贝构造函数和析构函数\\" id=\\"markdown-toc-23构造函数拷贝构造函数和析构函数\\">2.3、构造函数、拷贝构造函数和析构函数</a></li>\\n </ul>\\n </li>\\n </ul>\\n </li>\\n <li><a href=\\"#四amfreader\\" id=\\"markdown-toc-四amfreader\\">四、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code></a> <ul>\\n <li><a href=\\"#1objectdef\\" id=\\"markdown-toc-1objectdef\\">1、<code class=\\"language-plaintext highlighter-rouge\\">ObjectDef</code></a></li>\\n <li><a href=\\"#2amfreader-定义\\" id=\\"markdown-toc-2amfreader-定义\\">2、<code class=\\"language-plaintext highlighter-rouge\\">AMFReader</code> 定义</a> <ul>\\n <li><a href=\\"#21构造函数析构函数\\" id=\\"markdown-toc-21构造函数析构函数\\">2.1、构造函数、析构函数</a></li>\\n <li><a href=\\"#22简单封装-packetreader-的一些函数\\" id=\\"markdown-toc-22简单封装-packetreader-的一些函数\\">2.2、简单封装 <code class=\\"language-plaintext highlighter-rouge\\">PacketReader</code> 的一些函数</a></li>\\n <li><a href=\\"#23设置-gptr-位置\\" id=\\"markdown-toc-23设置-gptr-位置\\">2.3、设置 <code class=\\"language-plaintext highlighter-rouge\\">gptr</code> 位置</a></li>\\n <li><a href=\\"#24判断类型\\" id=\\"markdown-toc-24判断类型\\">2.4、判断类型</a></li>\\n </ul>\\n </li>\\n <li><a href=\\"#3解析-as3-null\\" id=\\"markdown-toc-3解析-as3-null\\">3、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Null</code></a></li>\\n <li><a href=\\"#4解析-as3-number\\" id=\\"markdown-toc-4解析-as3-number\\">4、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Number</code></a></li>\\n <li><a href=\\"#5解析-as3-integer\\" id=\\"markdown-toc-5解析-as3-integer\\">5、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Integer</code></a></li>\\n <li><a href=\\"#6解析-as3-boolean\\" id=\\"markdown-toc-6解析-as3-boolean\\">6、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Boolean</code></a></li>\\n <li><a href=\\"#7开始引用与结束引用\\" id=\\"markdown-toc-7开始引用与结束引用\\">7、开始引用与结束引用</a></li>\\n <li><a href=\\"#8解析-as3-bytearray\\" id=\\"markdown-toc-8解析-as3-bytearray\\">8、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code></a></li>\\n <li><a href=\\"#9解析-as3-date\\" id=\\"markdown-toc-9解析-as3-date\\">9、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Date</code></a></li>\\n <li><a href=\\"#10解析-as3-dictionary\\" id=\\"markdown-toc-10解析-as3-dictionary\\">10、解析 AS3 <code class=\\"language-plaintext highlighter-rouge\\">Dictionary</code></a></li>\\n </ul>\\n </li>\\n</ul>\\n\\n<p>本文是麦克船长《OpenRTMFP/Cumulus 原理、源码及实践》系列文章的其中一篇,相关内容最初首发于 CSDN 的 Poechant 技术博客,后整理于本博客。本篇文章主要介绍 ActionScript 独有的 AMF 数据格式,并对其序列化和反序列化的源码进行详细解读。</p>\\n\\n<h3 id=\\"一amf-数据类型定义\\">一、AMF 数据类型定义</h3>\\n\\n<h4 id=\\"1数据类型\\">1、数据类型</h4>\\n\\n<p>各种数据类型的标示都在 AMF.h 中定义为宏</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"cp\\">#define AMF_NUMBER 0x00 // 浮点数\\n#define AMF_BOOLEAN 0x01 // 布尔型\\n#define AMF_STRING 0x02 // 字符串\\n#define AMF_BEGIN_OBJECT 0x03 // 对象,开始\\n#define AMF_NULL 0x05 // null\\n#define AMF_UNDEFINED 0x06\\n#define AMF_REFERENCE 0x07\\n#define AMF_MIXED_ARRAY 0x08\\n#define AMF_END_OBJECT 0x09 // 对象,结束\\n#define AMF_BEGIN_TYPED_OBJECT 0x10\\n#define AMF_STRICT_ARRAY 0x0A\\n#define AMF_DATE 0x0B // 日期\\n#define AMF_LONG_STRING 0x0C // 字符串\\n#define AMF_UNSUPPORTED 0x0D\\n</span> \\n<span class=\\"cp\\">#define AMF_AVMPLUS_OBJECT 0x11\\n#define AMF_END 0xFF\\n</span> \\n<span class=\\"cp\\">#define AMF3_UNDEFINED 0x00\\n#define AMF3_NULL 0x01\\n#define AMF3_FALSE 0x02\\n#define AMF3_TRUE 0x03\\n#define AMF3_INTEGER 0x04\\n#define AMF3_NUMBER 0x05\\n#define AMF3_STRING 0x06\\n#define AMF3_DATE 0x08\\n#define AMF3_ARRAY 0x09\\n#define AMF3_OBJECT 0x0A\\n#define AMF3_BYTEARRAY 0x0C\\n#define AMF3_DICTIONARY 0x11\\n</span></code></pre></div></div>\\n\\n<p>并定义了一个枚举类表示数据类型:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">AMF</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"k\\">enum</span> <span class=\\"n\\">Type</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Null</span><span class=\\"o\\">=</span><span class=\\"mi\\">0</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Boolean</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Integer</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Number</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">String</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Date</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Array</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Object</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">ByteArray</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">Dictionary</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">RawObjectContent</span><span class=\\"p\\">,</span>\\n <span class=\\"n\\">End</span>\\n <span class=\\"p\\">};</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<h4 id=\\"2undefined-type\\">2、<code class=\\"language-plaintext highlighter-rouge\\">undefined</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">undefined</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"3null-type\\">3、<code class=\\"language-plaintext highlighter-rouge\\">null</code> Type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">null</code> 类型标记表示。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"4false-type\\">4、<code class=\\"language-plaintext highlighter-rouge\\">false</code> type</h4>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型由 <code class=\\"language-plaintext highlighter-rouge\\">false</code> 类型标记表示,用于编码布尔值 <code class=\\"language-plaintext highlighter-rouge\\">false</code>。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"5true-type\\">5、<code class=\\"language-plaintext highlighter-rouge\\">true</code> type</h4>\\n\\n<p>true 类型由 true 类型标记表示,用于编码布尔值 true。注意,在 ActionScript 3.0 中,布尔值的原始形式和对象形式不存在。此值不会编码任何其他信息。</p>\\n\\n<h4 id=\\"6integer-type\\">6、<code class=\\"language-plaintext highlighter-rouge\\">integer</code> type</h4>\\n\\n<p>在 AMF 3 中,整数使用可变长度的无符号 29 位整数进行序列化。ActionScript 3.0 中的整数类型 - 有符号 <code class=\\"language-plaintext highlighter-rouge\\">int</code> 类型和无符号 <code class=\\"language-plaintext highlighter-rouge\\">uint</code> 类型 - 也使用 29 位在 AVM+中表示。如果无符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">uint</code>) 的值大于等于 229 或者如果有符号整数 (<code class=\\"language-plaintext highlighter-rouge\\">int</code>) 的值大于等于 228,则它将被 AVM+ 表示为 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型,并使用 AMF 3 double 类型进行序列化。</p>\\n\\n<h4 id=\\"7double-type\\">7、<code class=\\"language-plaintext highlighter-rouge\\">double</code> type</h4>\\n\\n<p>AMF 3 的 <code class=\\"language-plaintext highlighter-rouge\\">double</code> 类型与 AMF 0 的 <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 类型编码方式相同。此类型用于编码 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">Number</code> 或值大于等于 228 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">int</code> 或值大于等于 229 的 ActionScript <code class=\\"language-plaintext highlighter-rouge\\">uint</code>。编码值始终是网络字节顺序中的 8 字节 IEEE-754 双精度浮点值 (低内存中的符号位)。</p>\\n\\n<h4 id=\\"8string-type\\">8、<code class=\\"language-plaintext highlighter-rouge\\">String</code> type</h4>\\n\\n<p>ActionScript String 值使用 AMF 3 中的单个 string 类型表示 - AMF 0 中的 <code class=\\"language-plaintext highlighter-rouge\\">string</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">long string</code> 类型的概念不再使用。可以使用对隐式字符串引用表中的索引将字符串作为先前发生的字符串的引用发送。字符串使用 UTF-8 编码 - 但是头可以描述字符串文本或字符串引用。空字符串永远不会作为引用发送。</p>\\n\\n<h4 id=\\"9xmldocument-type\\">9、<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了新的 XML 类型 (参见 3.13),但是旧版的 XMLDocument 类型在语言中被保留为 <code class=\\"language-plaintext highlighter-rouge\\">flash.xml.XMLDocument</code>。与 AMF 0 类似,<code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 的结构需要扁平化为字符串表示以进行序列化。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。XMLDocuments 可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">XMLDocument</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"10date-type\\">10、<code class=\\"language-plaintext highlighter-rouge\\">Date</code> type</h4>\\n\\n<p>在 AMF 3 中,ActionScript Date 简单地作为自 1970 年 1 月 1 日午夜 (UTC 时区) 以来的毫秒数进行序列化。不发送本地时区信息。可以使用对隐式对象引用表中的索引将日期作为先前发生的日期实例的引用发送。</p>\\n\\n<h4 id=\\"11array-type\\">11、<code class=\\"language-plaintext highlighter-rouge\\">Array</code> type</h4>\\n\\n<p>ActionScript 数组的类型和在数组中的位置是基于它们的索引性质描述的。以下表格概述了这些术语的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">strict</code>:仅包含序数(数字)索引</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">dense</code>:序数索引从 0 开始,并且在连续索引之间不存在间隙(即,从 0 到数组长度的每一个索引都被定义了)</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">sparse</code>:包含至少两个索引之间的一个间隙</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">associative</code>:包含至少一个非序数(字符串)索引(有时称为 ECMA 数组)</li>\\n</ul>\\n\\n<p>AMF 将数组分为两部分,密集部分和关联部分。关联部分的二进制表示由名称/值对(可能没有)终止的空字符串。密集部分的二进制表示由密集部分的大小(可能为零)以及有序的值列表(可能没有)组成。在 AMF 中写入的顺序是密集部分的大小,一个以空字符串终止的名称/值对列表,然后是大小的值。数组可以通过使用隐式对象引用表的索引作为先前发生的数组的引用来发送。</p>\\n\\n<h4 id=\\"12object-type\\">12、<code class=\\"language-plaintext highlighter-rouge\\">Object</code> type</h4>\\n\\n<p>AMF 3 中有一种类型用于处理 ActionScript 对象和自定义用户类。使用术语 “traits” 来描述类的定义特征。除了 “anonymous” 对象和 “typed” 对象,ActionScript 3.0 还引入了两个进一步的 traits 来描述如何序列化对象,即 “dynamic” 和 “externalizable”。以下表格概述了这些术语和它们的含义:</p>\\n\\n<ul>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Anonymous</code>:实际的 ActionScript 对象类型的实例或没有注册别名的类的实例(在反序列化时将其视为对象)。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Typed</code>:具有注册别名的类的实例。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Dynamic</code>:具有动态特征声明的类定义的实例;可以在运行时动态地从实例中添加和删除公共变量成员。</li>\\n <li><code class=\\"language-plaintext highlighter-rouge\\">Externalizable</code>:实现 flash.utils.IExternalizable 的类的实例,它完全控制其成员的序列化(特征信息中不包含属性名)。</li>\\n</ul>\\n\\n<p>在这些特征之外,对象的特征信息还可能包括在类上定义的一组公共变量和公共可读写属性名称(即不是函数的公共成员)。成员名称的顺序很重要,因为在特征信息之后的成员值将按照完全相同的顺序出现。这些成员被视为密封成员,因为它们是由类型明确定义的。</p>\\n\\n<p>如果类型是动态的,则在密封成员之后可以包括一个进一步的部分,该部分将动态成员列为名称/值对。当遇到空字符串名称时,继续读取动态成员。</p>\\n\\n<p>对象可以通过使用隐式对象引用表中的索引来作为先前发生对象的引用。此外,还可以通过使用隐式特征引用表中的索引将特征信息发送为先前发生的一组特征的引用。</p>\\n\\n<h4 id=\\"13xml-type\\">13、<code class=\\"language-plaintext highlighter-rouge\\">XML</code> type</h4>\\n\\n<p>ActionScript 3.0 引入了一种新的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型,支持 E4X 语法。为了序列化,需要将 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 类型展平成字符串表示形式。与 AMF 中的其他字符串一样,内容使用 UTF-8 编码。<code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 XML 实例的引用发送。请注意,这种编码对 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 的使用造成了一些理论限制。每个 UTF-8 编码的 <code class=\\"language-plaintext highlighter-rouge\\">XML</code> 实例的字节长度最大为 228-1 字节(大约 256 MB)。</p>\\n\\n<h4 id=\\"14bytearray-type\\">14、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> type</h4>\\n\\n<p>用于保存字节数组,即 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>。AMF 3 使用可变长度编码 29 位整数序列化此类型,其中包括字节长度前缀,然后是 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 的原始字节。<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例可以通过使用对隐式对象引用表中的索引作为先前发生的 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实例的引用发送。</p>\\n\\n<h4 id=\\"15amf3-的使用\\">15、AMF3 的使用</h4>\\n\\n<h5 id=\\"151netconnection-and-amf-3\\">15.1、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> and AMF 3</h5>\\n\\n<p>除了序列化 ActionScript 类型外,AMF 还可用于远程服务的异步调用。可使用简单的消息结构将一批请求发送到远程端点。此消息结构的格式为 AMF 0(参见[AMF0])。可以使用特殊的 <code class=\\"language-plaintext highlighter-rouge\\">avmplus-object-marker</code> 类型将上下文头值或消息正文切换到 AMF 3 编码。</p>\\n\\n<h5 id=\\"152netconnection-in-actionscript-30\\">15.2、<code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> in ActionScript 3.0</h5>\\n\\n<p>在 ActionScript 3.0 中,NetConnection 的限定类名是 flash.net.NetConnection。这个类仍然使用响应器来处理远程端点的结果和状态响应,但是现在需要强类型的 Responder 类。完全限定的类名是 flash.net.Responder。除了正常的结果和状态响应之外,NetConnection 还会分发事件,开发人员可以添加监听器。下面是这些事件的概述:</p>\\n\\n<ul>\\n <li>当异常异步抛出时触发,例如来自本机异步代码。</li>\\n <li>当输入或输出错误导致网络操作失败时触发。</li>\\n <li>当 NetConnection 对象报告其状态或错误条件时触发。</li>\\n <li>如果对 NetConnection.call() 的调用尝试连接到调用者安全沙箱外的服务器,则会触发。</li>\\n</ul>\\n\\n<h5 id=\\"153bytearray-idatainput-and-idataoutput\\">15.3、<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code>, <code class=\\"language-plaintext highlighter-rouge\\">IDataInput</code> and <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput</code></h5>\\n\\n<p>ActionScript 3.0 引入了一种新类型,用于支持以字节数组形式处理原始数据,即 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.ByteArray</code>。为了协助 ActionScript 对象序列化和复制,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 实现了 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataInput</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">flash.utils.IDataOutput</code>。这些接口指定了帮助将常见类型写入字节流的实用方法。两个感兴趣的方法是 <code class=\\"language-plaintext highlighter-rouge\\">IDataOutput.writeObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">IDataInput.readObject</code>。这些方法使用 AMF 编码对象。使用的 AMF 版本由 <code class=\\"language-plaintext highlighter-rouge\\">ByteArray.objectEncoding</code> 方法控制,该方法可以设置为 AMF 3 或 AMF 0。枚举类型 <code class=\\"language-plaintext highlighter-rouge\\">flash.net.ObjectEncoding</code> 包含 AMF 版本的常量:分别为 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF0</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">ObjectEncoding.AMF3</code>。</p>\\n\\n<p>请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray.writeObject</code> 使用一个版本的 AMF 对整个对象进行编码。与 <code class=\\"language-plaintext highlighter-rouge\\">NetConnection</code> 不同,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 不会从 AMF 0 开始,然后将 <code class=\\"language-plaintext highlighter-rouge\\">objectEncoding</code> 属性设置为 AMF 3 并切换到 AMF 3。还请注意,<code class=\\"language-plaintext highlighter-rouge\\">ByteArray</code> 为每个 <code class=\\"language-plaintext highlighter-rouge\\">readObject</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">writeObject</code> 调用使用新的对象、对象特征和字符串的隐式引用表。</p>\\n\\n<h3 id=\\"二binaryreaderwriter\\">二、<code class=\\"language-plaintext highlighter-rouge\\">BinaryReader/Writer</code></h3>\\n\\n<h4 id=\\"1amf3-数据格式基础\\">1、AMF3 数据格式基础</h4>\\n\\n<p>首先介绍一下变长整数(Variable Length Integer),比如 UInt32 如下。</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-1.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档,这是一种压缩方式的整数存储,且每一字节都对后面的数据具有预知作用。那么字符串如何处理呢?下面是字符串的处理方式,AMF0 和 AMF3 都才用 UTF-8 编码方式,并做如下压缩处理:</p>\\n\\n<p><img src=\\"/img/src/2012-04-24-openrtmfp-cumulus-4-2.png\\" alt=\\"image\\" /></p>\\n\\n<p>上图摘自 Adobe AMF3 官方文档。</p>\\n\\n<h4 id=\\"2序列化\\">2、序列化</h4>\\n\\n<p>序列化包括 8 位、16 位、32 位,以及 UTF-8 和 UTF-16(I guess)编码的 String,还有原生数据(Raw Data)、变长无符号整数(Variable Length Unsigned Integer)以及 IP 地址。所谓序列化就是按照指定格式编写各种对象、基础数据类型值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryWriter</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">writeAddress</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">Net</span><span class=\\"o\\">::</span><span class=\\"n\\">SocketAddress</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">,</span><span class=\\"kt\\">bool</span> <span class=\\"n\\">publicFlag</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryWriter</span> <span class=\\"n\\">BinaryWriterNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>请注意其中名为 <code class=\\"language-plaintext highlighter-rouge\\">BinaryWriterNull</code> 的成员。构造函数定义为:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">ostr</span><span class=\\"p\\">)</span><span class=\\"o\\">:</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">(</span><span class=\\"n\\">ostr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n\\n<span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryWriter</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">flush</span><span class=\\"p\\">();</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>其中 <code class=\\"language-plaintext highlighter-rouge\\">writeRaw</code> 是简单地封装 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter::writeRaw()</code>,如下:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入整数实现如下,用的是从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的重载运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span> \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入变长整数,这段代码含义也一目了然,就是读取变长无符号 32 位整数、64 位整数。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">false</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">21</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span> <span class=\\"c1\\">// 4 bytes maximum</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">22</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"nb\\">true</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write7BitLongValue</span><span class=\\"p\\">(</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">shift</span> <span class=\\"o\\">=</span> <span class=\\"p\\">(</span><span class=\\"n\\">Util</span><span class=\\"o\\">::</span><span class=\\"n\\">Get7BitValueSize</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">)</span><span class=\\"o\\">-</span><span class=\\"mi\\">1</span><span class=\\"p\\">)</span><span class=\\"o\\">*</span><span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">max</span> <span class=\\"o\\">=</span> <span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">63</span><span class=\\"p\\">;</span> <span class=\\"c1\\">// Can give 10 bytes!</span>\\n <span class=\\"k\\">if</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span><span class=\\"p\\">)</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">shift</span><span class=\\"p\\">;</span>\\n \\n <span class=\\"k\\">while</span><span class=\\"p\\">(</span><span class=\\"n\\">shift</span><span class=\\"o\\">&gt;=</span><span class=\\"mi\\">7</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"mh\\">0x80</span> <span class=\\"o\\">|</span> <span class=\\"p\\">((</span><span class=\\"n\\">value</span><span class=\\"o\\">&gt;&gt;</span><span class=\\"n\\">shift</span><span class=\\"p\\">)</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">));</span>\\n <span class=\\"n\\">shift</span> <span class=\\"o\\">-=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">max</span> <span class=\\"o\\">?</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0xFF</span> <span class=\\"o\\">:</span> <span class=\\"n\\">value</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写入 IP 地址的两个函数暂略。</p>\\n\\n<h4 id=\\"3反序列化\\">3、反序列化</h4>\\n\\n<p>反序列化就是从指定格式的数据中读出各类型的数据值。</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"k\\">class</span> <span class=\\"nc\\">BinaryReader</span> <span class=\\"o\\">:</span> <span class=\\"k\\">public</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span> <span class=\\"p\\">{</span>\\n<span class=\\"nl\\">public:</span>\\n <span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">);</span>\\n <span class=\\"k\\">virtual</span> <span class=\\"o\\">~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">();</span>\\n \\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt64</span> <span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read7BitEncoded</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString8</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"kt\\">void</span> <span class=\\"n\\">readString16</span><span class=\\"p\\">(</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">read16</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">read32</span><span class=\\"p\\">();</span>\\n <span class=\\"kt\\">bool</span> <span class=\\"n\\">readAddress</span><span class=\\"p\\">(</span><span class=\\"n\\">Address</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">address</span><span class=\\"p\\">);</span>\\n \\n <span class=\\"k\\">static</span> <span class=\\"n\\">BinaryReader</span> <span class=\\"n\\">BinaryReaderNull</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">};</span>\\n</code></pre></div></div>\\n\\n<p>构造与析构函数都很简单:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istream</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">istr</span><span class=\\"p\\">)</span> <span class=\\"o\\">:</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">(</span><span class=\\"n\\">istr</span><span class=\\"p\\">,</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">NETWORK_BYTE_ORDER</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::~</span><span class=\\"n\\">BinaryReader</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取原生数据(Raw Data):</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">((</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">std</span><span class=\\"o\\">::</span><span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">readRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">,</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写整数,用的是 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryWriter</code> 的重载运算符:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">write32</span><span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&lt;&lt;</span> <span class=\\"n\\">value</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读写整数依旧使用从 <code class=\\"language-plaintext highlighter-rouge\\">Poco::BinaryReader</code> 继承来的运算符操作:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt8</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read8</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt16</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read16</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt16</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n \\n<span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read32</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">(</span><span class=\\"o\\">*</span><span class=\\"k\\">this</span><span class=\\"p\\">)</span> <span class=\\"o\\">&gt;&gt;</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">c</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>写字符串:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt8</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString8</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write8</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"kt\\">char</span><span class=\\"o\\">*</span> <span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">UInt16</span> <span class=\\"n\\">size</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">,</span><span class=\\"n\\">size</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n<span class=\\"kt\\">void</span> <span class=\\"n\\">BinaryWriter</span><span class=\\"o\\">::</span><span class=\\"n\\">writeString16</span><span class=\\"p\\">(</span><span class=\\"k\\">const</span> <span class=\\"n\\">string</span><span class=\\"o\\">&amp;</span> <span class=\\"n\\">value</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">write16</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">.</span><span class=\\"n\\">size</span><span class=\\"p\\">());</span>\\n <span class=\\"n\\">writeRaw</span><span class=\\"p\\">(</span><span class=\\"n\\">value</span><span class=\\"p\\">);</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p>读取变长整数,分别针对 <code class=\\"language-plaintext highlighter-rouge\\">UInt32</code> 和 <code class=\\"language-plaintext highlighter-rouge\\">UInt64</code>,要理解 <code class=\\"language-plaintext highlighter-rouge\\">AMF3</code> 的变长整数才能理解这个:</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt32</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt32</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">3</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code><span class=\\"n\\">UInt64</span> <span class=\\"n\\">BinaryReader</span><span class=\\"o\\">::</span><span class=\\"n\\">read7BitLongValue</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">n</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">UInt8</span> <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"n\\">UInt64</span> <span class=\\"n\\">result</span> <span class=\\"o\\">=</span> <span class=\\"mi\\">0</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">while</span> <span class=\\"p\\">((</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x80</span><span class=\\"p\\">)</span> <span class=\\"o\\">&amp;&amp;</span> <span class=\\"n\\">n</span> <span class=\\"o\\">&lt;</span> <span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"p\\">{</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"mi\\">7</span><span class=\\"p\\">;</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"p\\">(</span><span class=\\"n\\">b</span><span class=\\"o\\">&amp;</span><span class=\\"mh\\">0x7F</span><span class=\\"p\\">);</span>\\n <span class=\\"n\\">b</span> <span class=\\"o\\">=</span> <span class=\\"n\\">read8</span><span class=\\"p\\">();</span>\\n <span class=\\"o\\">++</span><span class=\\"n\\">n</span><span class=\\"p\\">;</span>\\n <span class=\\"p\\">}</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">&lt;&lt;=</span> <span class=\\"p\\">((</span><span class=\\"n\\">n</span><span class=\\"o\\">&lt;</span><span class=\\"mi\\">8</span><span class=\\"p\\">)</span> <span class=\\"o\\">?</span> <span class=\\"mi\\">7</span> <span class=\\"o\\">:</span> <span class=\\"mi\\">8</span><span class=\\"p\\">);</span> <span class=\\"c1\\">// Use all 8 bits from the 4th byte</span>\\n <span class=\\"n\\">result</span> <span class=\\"o\\">|=</span> <span class=\\"n\\">b</span><span class=\\"p\\">;</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">result</span><span class=\\"p\\">;</span>\\n<span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<h3 id=\\"三packetreaderwriter\\">三、<code class=\\"language-plaintext highlighter-rouge\\">PacketReader/Writer</code></h3>\\n\\n<h4 id=\\"1packetreader\\">1、PacketReader</h4>\\n\\n<div class=\\"language-plaintext highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code>#define PACKETRECV_SIZE 2048\\nclass PacketReader: public BinaryReader {\\npublic:\\n PacketReader(const Poco::UInt8* buffer,Poco::UInt32 size);\\n PacketReader(PacketReader&amp;);\\n virtual ~PacketReader();\\n const Poco::UInt32 fragments;\\n Poco::UInt32 available(); // 可读字节数\\n Poco::UInt8* current();\\n Poco::UInt32 position(); // 获取当前的相对位置(相对于起始位置的)\\n void reset(Poco::UInt32 newPos = 0); // 设定当前位置\\n void shrink(Poco::UInt32 rest);\\n void next(Poco::UInt32 size);\\nprivate:\\n MemoryInputStream _memory;\\n};\\n</code></pre></div></div>\\n\\n<h6 id=\\"11封装-memoryinputstream\\">1.1、封装 <code class=\\"language-plaintext highlighter-rouge\\">MemoryInputStream</code></h6>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">available</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">available</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">available</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">current</code>:当前绝对位置(内存地址)</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"p\\">(</span><span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt8</span><span class=\\"o\\">*</span><span class=\\"p\\">)</span><span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">position</code>:当前位置(绝对位置)减去缓冲区起始位置</p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"n\\">Poco</span><span class=\\"o\\">::</span><span class=\\"n\\">UInt32</span> <span class=\\"n\\">PacketReader</span><span class=\\"o\\">::</span><span class=\\"n\\">position</span><span class=\\"p\\">()</span> <span class=\\"p\\">{</span>\\n <span class=\\"k\\">return</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">current</span><span class=\\"p\\">()</span> <span class=\\"o\\">-</span> <span class=\\"n\\">_memory</span><span class=\\"p\\">.</span><span class=\\"n\\">begin</span><span class=\\"p\\">();</span>\\n <span class=\\"p\\">}</span>\\n</code></pre></div></div>\\n\\n<p><code class=\\"language-plaintext highlighter-rouge\\">reset</code></p>\\n\\n<div class=\\"language-c++ highlighter-rouge\\"><div class=\\"highlight\\"><pre class=\\"highlight\\"><code> <span class=\\"kr\\">inline</span> <span class=\\"kt\\">void</span> <span class=\\"n\\">PacketReader&